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;
...
我想知道我们现在是否可以重写该方法签名而无需重载?即我们能否表示返回值是所有输入参数的交集,还是仍然不可能?
我正在使用的功能需要非常相似的东西。
答案 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; }