Suppose I have a type describing a supplier of some type T
in TypeScript:
type Supplier<T> = () => T;
Now I have a function f
which takes an arbitrary number of Supplier
objects with differing return types, which I could call like
const values = f(() => 3, () => "text", () => true);
Is there any chance to define f
such that the compiler understands that values
is of type [number, string, boolean]
?
NOTE: I am not after a function with three parameters. I want to define f
to have an arbitrary number of arguments.
function f(...args: ????[]): ....Parameters<typeof f> {...}
Yes, you can map over tuple types so that if the function return type is intended to be of the generic type T
(in your example this would be the tuple type [number, string, boolean]
) then the function parameter list is of a mapped type {[I in keyof T]: Supplier<T[I]>}
where every element of T
at index I
is mapped to Supplier
of that element type (in your example this would be the tuple type [Supplier<number>, Supplier<string>, Supplier<boolean>
):
declare function f<T extends any[]>(...args: { [I in keyof T]: Supplier<T[I]> }): T;
const values = f(() => 3, () => "text", () => true);
// ^? const values: [number, string, boolean]
Notice how for homomorphic mapped types (What does "homomorphic mapped type" mean?) like the above, TypeScript can "reverse" the mapping to infer the output from the input. So you can call f()
with an argument list of type [() => number, () => string, () => boolean]
, and TypeScript can match it to {[I in keyof T]: Supplier<T[I]>}
as [Supplier<number>, Supplier<string>, Supplier<boolean>]
, and therefore that T
must be [number, string, boolean]
. This "reverse" mapped type inference isn't documented in the current version of the TS Handbook, but the older documentation about it still applies.