简体   繁体   English

TypeScript中的可变参数泛型类型?

[英]Variadic generic types in TypeScript?

I'd like to make a function in TypeScript that takes an array of constructor functions and returns a corresponding array of instances. 我想在TypeScript中创建一个函数,该函数接受构造函数的数组并返回相应的实例数组。 See code below. 请参见下面的代码。

Note that the getArray method is wildly incorrect, it is just an attempt to convey my intent. 请注意, getArray方法非常不正确,这只是在传达我的意图。

Is this possible in any form or is it beyond the capabilities of TypeScript's type engine? 这可能是任何形式还是超出TypeScript的类型引擎的能力?

class T1 {}
class T2 {}
class T3 {}
type AnyCon = new() => any;

function getOne<T_Con extends AnyCon>(con: T_Con): InstanceType<T_Con> {
  return new con();
}

function getArray<T_Cons>(cons: T_Cons): InstanceType<T_Cons>[] {
  return cons.map( (c: con) => new con() );
}

let t1: T1 = getOne(T1);
let [t2, t3]: [T2, T3] = getArray( [T2, T3] );

You can do this in TS3.1 and above using mapped array/tuple types . 您可以在TS3.1及更高版本中使用映射的数组/元组类型执行此操作 It's easier to get tuples inferred for rest parameters than it is for array parameters, so I'll show that instead: 获取剩余参数的 元组要比数组参数的元组容易,因此,我将显示为:

function getVariadic<T extends Array<AnyCon>>(...cons: T): {
  [K in keyof T]: T[K] extends AnyCon ? InstanceType<T[K]> : never
};
function getVariadic(...cons: AnyCon[]): any[] {
  return cons.map((c: AnyCon) => new c());
}

let [t2, t3]: [T2, T3] = getVariadic(T2, T3);

This behaves as you expect, I think. 我认为这符合您的预期。 Hope that helps. 希望能有所帮助。 Good luck! 祝好运!


EDIT: if you really need to do it with an array it's easy to type the function but a little harder to call it in such a way that the order of the array is preserved as a tuple: 编辑:如果您确实需要使用数组来执行此操作,则可以轻松键入该函数,但以将数组的顺序保留为元组的方式来调用它会有点困难:

function getArray<T extends Array<AnyCon>>(cons: T): {
  [K in keyof T]: T[K] extends AnyCon ? InstanceType<T[K]> : never
};
function getArray(cons: AnyCon[]): any[] {
  return cons.map((c: AnyCon) => new c());
}

// error, getArray([T2, T3]) returns (T2 | T3)[]!
let [t2, t3]: [T2, T3] = getArray([T2, T3]); // 🙁

Oops let's try again 糟糕,我们再试一次

// okay, but verbose and redundant
let [t2, t3]: [T2, T3] = getArray([T2, T3] as [typeof T2, typeof T3]); // 😐

Well maybe we can use a helper function to get [T2, T3] inferred as a tuple type: 好吧,也许我们可以使用辅助函数来将[T2, T3]推断为元组类型:

// put this in a library somewhere
type Lit = string | number | boolean | undefined | null | void | {};
const tuple = <T extends Lit[]>(...args: T) => args;

// works! but now is very similar to getVariadic
let [t2, t3]: [T2, T3] = getArray(tuple(T2, T3)); 🙂

Finally, once TypeScript 3.4 lands there will be const contexts which are a bit easier but require some more changes to getArray() : 最后,一旦TypeScript 3.4登陆,就会有const上下文 ,这会更容易一些,但需要对getArray()进行更多更改:

// note the widening to ReadonlyArray
function getArray<T extends ReadonlyArray<AnyCon>>(cons: T): {
  -readonly [K in keyof T]: T[K] extends AnyCon ? InstanceType<T[K]> : never
};
function getArray(cons: AnyCon[]): any[] {
  return cons.map((c: AnyCon) => new c());
}

// works and is the least repetitive
let [t2, t3]: [T2, T3] = getArray([T2, T3] as const); 😀

But as I said above, getVariadic() is probably the most straightforward way to get this behavior right now. 但是正如我上面所说, getVariadic()可能是目前实现此行为的最直接的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM