打字稿中的Varargs

时间:2018-06-06 20:43:21

标签: typescript

我想将不同类型的任意数量的参数传递给函数并使用这些类型。看起来like following

function f<A>(a: A): A;
function f<A, B>(a: A, b: B): A & B;
function f<A, B, C>(a: A, b: B, c: C): A & B & C;
function f<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;

function f(...args: any[]) {
    return Object.assign({}, ...args);
}

var smth = f({ x: 1 }, { y: 2 }, { z: 3 });
var res = smth.x + smth.y + smth.z;

由于我想要任意数量的参数,我想摆脱这些声明

function f<A>(a: A): A;
function f<A, B>(a: A, b: B): A & B;
function f<A, B, C>(a: A, b: B, c: C): A & B & C;
function f<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;

并使用单个声明,如:

function f<...T>(args: [...T]): &<...T>;

但这件事在语法上是错误的。

有没有办法以正确的方式重写它?

PS:Same question in Russian.

1 个答案:

答案 0 :(得分:1)

虽然其他人提到Proposal: Variadic Kinds会帮助完成此任务,但我们可以为您的具体示例找到一些解决方法。

如果我们使用单个类型参数编写函数签名,我们可以获得参数的联合类型:

function f<A>(...args: A[]): A {}
var smth = f({ x: 1 }, { y: 2 }, { z: 3 });
typeof smth = {
    x: number;
    y?: undefined;
    z?: undefined;
} | {
    y: number;
    x?: undefined;
    z?: undefined;
} | {
    z: number;
    x?: undefined;
    y?: undefined;
}

这种方法的问题在于,如果我们使用类而不是对象文字,编译器将拒绝推断联合并给我们一个错误。如果我们让rest参数去(...)并且只使用数组,编译器将推断出参数类型的并集:

function f<A>(args: A[]): A { /*…*/}
var smth = f([{ x: 1 }, new A(), new B()]); 
typeof smth == A | B | {
    x: number;
}

所以现在我们有一个类型的联合,但你想要一个交集。我们可以使用条件类型将并集转换为交集(请参阅this answer)

type UnionToIntersection<U> = 
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

function f<A>(args: A[]): UnionToIntersection<A> {
    return Object.assign({}, ...args);
}

class A { z: number }
class B { y: number }

var smth = f([{ x: 1 }, new A(), new B()]); // will have type  A & B & { x: number; }
var res = smth.x + smth.y + smth.z;

希望这会有所帮助,并且至少在我们获得可变类型之前为您提供可用的解决方法。