Reputation: 1284
I have an array of instances where execute returns a type, here are for example 2 classe whose instances are in the array:
class ClassBase<TReturn>
{
execute (): TReturn
}
class ReturnsString extends ClassBase<string>
{
execute () : string
}
class ReturnsNumber extends ClassBase<number>
{
execute () : number
}
const items:[ReturnsString,ReturnsNumber] = [new ReturnsString(), new ReturnsNumber()];
inside a method i will call execute in a loop for each item in tuple.
How to declare a method that
accepts items - items could be of any length and can contain various types, eg. could be [ReturnString] or [ReturnString,ReturnString,ReturnString,ReturnNumber,....]
returns type of the returning type tuple [string,string,string,number]
Upvotes: 2
Views: 517
Reputation: 67
I recently implemented a similar types:
type TransformTuple<Tuple extends (() => any)[], Returns extends any[] = []> =
Tuple extends [() => any, ...infer More] ?
More extends (() => any)[] ?
TransformTuple<More, [...Returns, ReturnType<Tuple[0]>]>
: never
: Returns
type Test = TransformTuple<[() => 1, () => "2"]>
/*
type Test = [1, "2"]
*/
//NOTE Only tuples are supported
//If use normal array like this:
type Test2 = TransformTuple<Array<() => "never">>
/*
//You only get an empty array
type Test2 = []
*/
Upvotes: 1
Reputation: 1224
nice question! It made me think a lot and use many advanced TS typing features, but I guess that this makes what you are looking for:
abstract class ClassBase<TReturn = any>
{
abstract execute (): TReturn
}
type FlattenIfArray<T> = T extends (infer R)[] ? R : T
type ExtendsOf<T> = T extends ClassBase<infer R> ? R : T
type ExecuteArrayReturn<T> = ExtendsOf<FlattenIfArray<T>>
function example<T extends ClassBase<ExecuteArrayReturn<T>>[]>(items: T): ExecuteArrayReturn<T> {
return items.map(item => item.execute()) as any
}
class ReturnsString extends ClassBase<string>
{
execute () : string {return 'a'}
}
class ReturnsNumber extends ClassBase<number>
{
execute () : number {return 1}
}
const items:[ReturnsString,ReturnsNumber] = [new ReturnsString(), new ReturnsNumber()];
const result = example(items);
I needed to create some types to extract the execute
function return value from instances of the ClassBase
heirs. FlattenIfArray
gets the type of the array items, ExtendsOf
get the type of the generic type that has been set to T
and ExecuteArrayReturn
join both of them.
When you implement your real function, don't forget about the as any
on the function return
. Otherwise, TS will think that you want to return an array of the provided items types union, for example: (string | number)[]
.
Upvotes: 0
Reputation: 249556
You can use mapped types to map over a tuple and extract the return type for each tuple item:
type ReturnsOfClassBase<T extends Record<number, ClassBase<any>>> = {
-readonly [P in keyof T] : T[P] extends ClassBase<infer R> ? R: never
}
function getReturns<T extends readonly ClassBase<any>[]>(p: T): ReturnsOfClassBase<T> {
const result = []
for(let r of p) {
result.push(r.execute());
}
return result as any
}
let r = getReturns(items)
Upvotes: 3