Reputation: 287
I have a type that determines whether a type is a tuple (exact length) or an array. I want to be able to use this condition to infer the type of a parameter:
type ANumber = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
type IsTuple<Type> = Type extends readonly unknown[] ? Type['length'] extends ANumber ? true : false : false;
function fn<T extends Record<keyof T, unknown>>() {
return function withConfig(config: {
[K in keyof T]: IsTuple<T[K]> extends true
? (...params: T[K]) => void
: (param: T[K]) => void
}) {};
}
fn<{ tupleType: [number, number], arrayType: number[] }>()({
tupleType: (one, two) => {},
arrayType: (arr) => {}
})
The actual function call works fine, one
, two
, and arr
are all inferred correctly. However, ? (...params: T[K]) => void
errors with:
A rest parameter must be of an array type
Yet...a tuple is an array type isn't it? How do I point the compiler in the right direction here?
Upvotes: 1
Views: 1708
Reputation: 328272
If you come upon a situation where you have a generic type XXX
that you know is or will be assignable to type YYY
but the compiler does not (and there is an error because of it), you can often work around the situation by replacing XXX
with Extract<XXX, YYY>
using the Extract<T, U>
utility type.
The compiler is able to understand that Extract<XXX, YYY>
is assignable to both XXX
and YYY
. Later, when the generic type is specified, then Extract<XXX, YYY>
will be exactly the same as whatever XXX
resolves to, assuming you were correct about XXX
being assignable to YYY
in the first place.
In your example, you have T[K]
and you know it is assignable to an array type like readonly unknown[]
but the compiler does not know this. (It doesn't perform the sort of higher-order type analysis to see that IsTuple<T>
is defined in such a way that IsTuple<T> extends true
implies that T
is an array type.) So you can replace T[K]
with Extract<T[K], readonly unknown[]>
:
function fn<T extends Record<keyof T, unknown>>() {
return function withConfig(config: {
[K in keyof T]: IsTuple<T[K]> extends true
? (...params: Extract<T[K], readonly unknown[]>) => void // okay
: (param: T[K]) => void
}) { };
}
That works, and the rest of your code should work more or less the same way as it did before.
Upvotes: 2