TypeScript 3参数列表交集类型

时间:2018-07-30 22:44:31

标签: typescript

TypeScript 3改进了参数列表。

我正在查看Object.assign的类型定义,它看起来像这样:

interface ObjectConstructor {
    assign<T, U>(target: T, source: U): T & U;
    assign<T, U, V>(target: T, source1: U, source2: V): T & U & V;
    assign<T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W;
    ...

我想知道我们现在是否可以重写该方法签名而无需重载?即我们能否表示返回值是所有输入参数的交集,还是仍然不可能?

我正在使用的功能需要非常相似的东西。

2 个答案:

答案 0 :(得分:10)

虽然我喜欢@ y2bd的答案,但我认为如果任何传入的参数本身就是联合类型,就会出现问题:

const eitherOr = Math.random() < 0.5 ? { c: "4" } : { c: 5 };
const notSoGood = assign({ a: "2" }, { b: "3" }, eitherOr);
notSoGood.c; // string & number ?!

您会发现eitherOr从并集变为相交,而notSoGood不是正确的类型。

一种解决方法是“装箱”和“取消装箱”元组,以便您可以区分多个参数类型的并集中的单参数联合:

type BoxedTupleTypes<T extends any[]> =
  { [P in keyof T]: [T[P]] }[Exclude<keyof T, keyof any[]>]
type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type UnboxIntersection<T> = T extends { 0: infer U } ? U : never;
declare function assign<T, S extends any[]>(
  target: T,
  ...sources: S
): T & UnboxIntersection<UnionToIntersection<BoxedTupleTypes<S>>>

现在您将拥有正确的类型:

 const notSoGood = assign({ a: "2" }, { b: "3" }, eitherOr);
 notSoGood.c // string | number

话虽这么说,但我不知道此版本是否有其自身的问题。更不用说交叉路口并不是您真正想要的。您宁愿overwrite properties if possible(该交点无法捕获)。因此,在建议更改标准库之前,我们可能需要考虑TypeScript必须提供给我们的最佳签名。

希望有帮助。祝你好运!

答案 1 :(得分:7)

让我们开始将类型的元组转换为元组中的类型并集:

type TupleTypes<T> = { [P in keyof T]: T[P] } extends { [key: number]: infer V } ? V : never;
type A = TupleTypes<[1, "hello", true]>; // === 1 | "hello" | true

然后我们从this answer借用,将类型的并集转换为类型的交集:

type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never;
type B = UnionToIntersection<A>; // === 1 & "hello" & true

现在我们有了,让我们为Object.assign写一个包装器:

function assign<T extends object, S extends object[]>(target: T, ...sources: S): T & UnionToIntersection<TupleTypes<S>> {
    return Object.assign(target, ...sources);
}

const good = assign({ a: "2" }, { b: "3" }, { c: "4" }, { d: "4" }, { g: "5" });
// const good: { a: string; } & { b: string; } & { c: string; } & { d: string; } & { g: string; }