首页 > 解决方案 > 将 ...args 映射到子类型的元组

问题描述

我有一个场景,我想要一个可以接受任意数量的通用对象参数的函数。

我希望结果返回一个元组,其中该对象的每个通用参数都是元组位置类型。

例子

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) 

如果我只返回传入的确切类型,我可以让该类型工作:

function ReturnSelf<T extends Array<Wrap<One|Two>>>(...args:T): typeof args{
    return args;
}

但我不确定如何索引['obj']类型。我想也许映射类型可以用来做到这一点,但我不太明白。

打字稿游乐场链接

标签: typescriptmapped-types

解决方案


是的,你可以使用映射类型来做到这一点,因为 TypeScript 3.1 引入 了映射元组/数组类型的能力。你可以用“前进”的方式来做,就像你在做的那样:

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]

或“反向”方式,使用映射类型的推断

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]

正如您所看到的,无论哪种方式都可以......无论哪种方式,编译器都无法理解args.map(i => i.obj)执行您正在执行的类型操作,因此您需要使用类型断言或等效的类型断言(例如使用单个重载签名)。

好的,希望有帮助。祝你好运!

链接到代码


推荐阅读