我知道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
}
他们极不可能想过六件事,因此,如果有某种替代,我可以提供,这很好,但是对于我一生,我无法使其正常工作。
谢谢您的帮助!
答案 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]
对我很好。希望能有所帮助;祝你好运!