打字稿中的可变参数

Qwe*_*tiy 9 typescript

我想将任意数量的不同类型的参数传递给一个函数并使用这些类型。它看起来如下

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;
Run Code Online (Sandbox Code Playgroud)

因为我想要任意数量的参数,所以我想去掉这些声明

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;
Run Code Online (Sandbox Code Playgroud)

并使用单个声明,如:

function f<...T>(args: [...T]): &<...T>;
Run Code Online (Sandbox Code Playgroud)

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

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

PS:同样的问题俄语。

Tit*_*mir 6

编辑 3.0

虽然原始答案是正确的,但自从我第一次给它打字稿已经改变了。在 typescript 3.0 中,可以在 rest 参数中使用元组来捕获元组中参数的类型

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

function f<A extends any[]>(...args: A): UnionToIntersection<A[number]> { return null! }
var smth = f({ x: 1 }, new A(), new B());  // will have type  A & B & { x: number; }
Run Code Online (Sandbox Code Playgroud)

原答案

虽然正如其他人提到的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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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;
Run Code Online (Sandbox Code Playgroud)

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