I have a scenario where I want to have a function that can accept any number of args of an generic object.
I want the result to return a tuple where each of the generic params of this object is the tuple position type.
Example
type Wrap<T> = {
obj: T;
}
function UnWrap<T extends Array<Wrap<One|Two>>>(...args:T){ //return type?
return args.map(i => i.obj);
}
type One = {
foo: string;
}
type Two = {
bar: string;
}
let one: Wrap<One> = {obj: {foo: 'abc'}}
let two: Wrap<Two> ={obj: {bar: 'abc'}}
// res type should be [One, Two, Two, One]
let res = UnWrap(one, two, two, one)
I can get the type to work if I just return the exact type passed in:
function ReturnSelf<T extends Array<Wrap<One|Two>>>(...args:T): typeof args{
return args;
}
But I'm not sure how to index the ['obj']
type. I think maybe mapped types can be used to do this, but I can't quite figure it out.
Yes, you can use mapped types to do this, ever since TypeScript 3.1 introduced the ability to map tuple/array types. You could either do it the "forward" way, like you were doing:
function UnWrap<T extends Array<Wrap<One | Two | Three>>>(...args: T) {
return args.map(i => i.obj) as {
[K in keyof T]: T[K] extends Wrap<infer U> ? U : never
};
}
let res2 = UnWrap(one, two, three, two); // [One, Two, Three, Two]
or the "reverse" way, using inference from mapped types:
function UnWrap2<T extends Array<One | Two | Three>>(
...args: { [K in keyof T]: Wrap<T[K]> }
) {
return args.map(i => i.obj) as T;
}
let res3 = UnWrap2(one, two, three, two); // [One, Two, Three, Two]
Either way will work, as you can see... and either way, the compiler won't be able to understand that args.map(i => i.obj)
performs the type manipulation you're doing, so you'll need to use a type assertion or the equivalent of one (such as using a single overload signature).
Okay, hope that helps. Good luck!