Search code examples
typescriptmapped-types

Mapping ...args to tuple of child type


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.

Typescript Playground link


Solution

  • 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!

    Link to code