具有这些类型的返回数组的TypeScript可变参数... args

时间:2018-11-23 01:23:36

标签: typescript

我知道TypeScript doesn't currently support variadic types,所以我想知道如何才能同时完成此任务。我正在建立一个图书馆,所以只要最终用户不必处理它们,我就不介意跳过一些麻烦的事情。

这是一些简化的伪代码:

type ConstructorBase<T> = {
  new (id: number): T;
  type: string
}

type Things = { [key: string]: Thing }

private _things: Things

getThings (id: number, ...ctors: ConstructorBase<Thing>): Thing[] { 
  return ctors.map((ctor) => this._things[ctor])
}

这个想法是,用户可以传入一些特殊构成的构造函数,然后获取与id关联的那些东西的实例。这很棒!但是...最终用户必须将Thing []转换为预期结果。

const [a, b] = get(15, ThingTypeA, ThingTypeB) as [ThingTypeA, ThingTypeB]
const [c, d, e] = get(15, ThingTypeC, ThingTypeD, ThingTypeE) as [ThingTypeC, ThingTypeD, ThingTypeE]

我想要的是让他们丢弃as ...以耗尽最终用户代码,并使TypeScript自动推断类型。

我想要这样的东西:

getThings <T extends Thing[]> (id: number, ...ConstructorBase<T>): infer[] 

实验

我尝试了各种各样的变体,可以想到并阅读所有人们谈论此问题的TypeScript问题,但无法弄清楚。我猜想我可以对重载执行某些操作,但似乎重载要求返回类型相同,而我需要返回可变长度的不同元组-它只能看到最短的实现,而不是最长的实现。

getThing <T, U> (id: number, t1: ConstructorBase<T>, t2?: ConstructorBase<U>): [T, U?]
getThing <T, U, V> (id: number, t1: ConstructorBase<T>, t2?: ConstructorBase<U>, t3?: ConstructorBase<V>): [T, U?, V?] {
  // implementation
}

他们极不可能想过六件事,因此,如果有某种替代,我可以提供,这很好,但是对于我一生,我无法使其正常工作。

谢谢您的帮助!

1 个答案:

答案 0 :(得分:3)

如果使用重载,则可能需要重新排序它们,以便最具体的重载(最难匹配)在前,最一般的重载(最容易匹配)在后。可选参数更容易匹配,因此您希望带有可选参数的签名稍后出现在签名列表中。但是您不需要重载。

从TS3.1开始,使用tuples in rest/spread positions接受输入rest参数,并使用mapped tuples/arrays将输入映射到输出,您应该能够在单个签名中获得所需的行为。

这是您可能使用的类型签名,我假设ConstructorBase像这样:

type ConstructorBase<T> = new (id: number) => T;

declare function getThings<C extends ConstructorBase<any>[]>(
  id: number, ...ctors: C
): { [K in keyof C]: C[K] extends ConstructorBase<infer T> ? T : never }

让我们尝试一下:

declare class Thing1 { constructor(id: number); }
declare class Thing2 { constructor(id: number); }

const things = getThings(123, Thing1, Thing2);
// const things: [Thing1, Thing2]

对我很好。希望能有所帮助;祝你好运!