Whenever you make a call
const example3 = await fetchData({
latitude: 49.1,
longitud: 11.5,
variables: ['rain'] // error
})
The issue arises because the input object is of the wrong type, specifically due to the absence of the longitude
property. Consequently, the compiler struggles to deduce the V
generic type argument and defaults to the default never
type. This leads the compiler to validate against Params<never>
. The input fails to match as a Params<never>
in two ways - missing longitude
and incorrect typing of variables
(not never[]
). It's worth noting that the absence of the longitud
property isn't really a type error, but rather an excess property check which might not be the primary concern. Excess properties are generally allowed; refer to extend and only specify known properties? for more insights.
Hence, your input faces two challenges with the compiler, leading it to prioritize flagging the variables
property. While this might not align with your expectations, it falls within the compiler's discretion on how to highlight errors.
This explains the reasoning behind the issue. To resolve it, I recommend making the fetchData()
function generic in the P
type of the params
input and utilizing that P
to compute V
. This approach prevents any unexpected never
inference until after inferring the type of params
. Here's how you can implement it:
export async function fetchData<P extends Params<Variable>>(params: P) {
type V = P extends {variables: (infer V extends Variable)[]} ? V : never
return {} as unknown as Data<V>
}
I've used a locally defined function-specific type
alias for V
, but you can inline it as well. Notice how V
defaults to never
if variables
is omitted from P
. While P
impacts V
, the reverse isn't true; irrespective of V
's outcome, P
remains unaffected, ensuring no unexpected fallback behavior anticipating never[]
for variables
.
Lets put it to the test:
const example1 = await fetchData({
latitude: 49.1,
longitude: 11.5,
variables: ["rain"]
})
console.log(example1.variables.rain) // valid
console.log(example1.variables.temperature) // error
const example2 = await fetchData({
latitude: 49.1,
longitude: 11.5
})
console.log(example2.variables.rain) // error
const example3 = await fetchData({
latitude: 49.1,
longitud: 11.5, // error, surplus property
variables: ['rain']
})
Seems like the expected behavior has been achieved.
Playground link to code