Reputation: 74
Is it possible for the code below to be typed correctly?
function arrayElementTypes(...array: Array<(() => string) | (() => number) | (() => {prop: string}) | (() => number[])>) {
/// .. do something
/// .. and then return an array with each functions result
return array.map(arg => arg())
}
const [varA, varB, varC] = arrayElementTypes( () => "", () => ({prop: "prop"}), () => [1,2,3] )
// how can this be typed appropriately so that the :
// varA: string
// varB: {prop: string}
// varC: number[]
Upvotes: 1
Views: 1789
Reputation: 2261
const [varA, varB, varC, varD]: Array<string | number | {prop: string} | number[]> = arrayElementTypes( () => "", () => ({prop: "prop"}), () => [1,2,3] )
Does this satisfy your requirements?
Upvotes: 0
Reputation: 74
Managed to do it with
type ReturnTypes<T extends Array<(...a: any[]) => any>> = {
[P in keyof T]: T[P] extends (...a: any[]) => infer R ? R : never
}
type CustomArrayElement = (() => string) | (() => number) | (() => {prop: string}) | (() => number[])
function arrayElementTypes<T extends CustomArrayElement[]>(...array: T): ReturnTypes<typeof array> {
return array.map(arg => arg())
}
const [varA, varB, varC] = arrayElementTypes( () => "", () => ({prop: "prop"}), () => [1,2,3] )
Thank you all for your help!!
Upvotes: 2
Reputation: 1171
I'm not optimistic that it can be typed correctly. Firstly, I think you are looking at a tuple, rather than an array, since the "array" of functions have different signatures, and you want the return type of arrayElementTypes
to correspond to the return type of each function in the "array", matching position-wise.
At the same time, I hesitate to say it's impossible outright, as I've seen amazing things that can be done using a combination of generics and conditional types.
Edit: I have come up with some "building-block" types that may help with the final answer, but you can see if you can piece them together :)
// any function
type Fn = () => unknown;
// a tuple/array of functions
type FnArr = readonly Fn[];
// the first function in your tuple of functions
type Head<T extends FnArr> = T extends [infer HeadFn, ...any[]] ? HeadFn : never;
// the rest of the functions in your tuple of functions
type Tail<T extends FnArr> = T extends [any, ...infer TailFns] ? TailFns : never;
Using the above building blocks, you can extract the return types of each function in your tuple of functions. This doesn't directly lend itself to a solution, but maybe some recursively defined conditional types (to generalise for arbitrary tuple of functions) can get you there :)
const [varA, varB, varC] = arrayElementTypes( () => "", () => ({prop: "prop"}), () => [1,2,3] )
// how can this be typed appropriately so that the :
// varA: string
// varB: {prop: string}
// varC: number[]
type ExampleFns = [ () => string, () => {prop: "prop"}, () => number[] ];
type TypeForVarA = ReturnType<Head<ExampleFns>>; // F1 = string
type TypeForVarB = ReturnType<Head<Tail<ExampleFns>>>; // F2 = {prop: "prop"}
type TypeForVarC = ReturnType<Head<Tail<Tail<ExampleFns>>>>; // F3 = number[]
Upvotes: 1