此功能必须支持任意数量的参数:
type Result<T extends object[]> = "???"
function merge<T extends object[]>(...sources: T): Result<T> {
return Object.assign({}, ...sources)
}
预期结果类型为(playground)
的示例输入type Expected = {
a: 2
b: 1 | 2
c?: 1
d?: 1 | 2
e: 2
f: 2
}
// The return type should match `Expected` exactly. No intersections please!
const result: Expected = merge(
{} as {
a: 1
b: 1
c?: 1
d?: 1
e?: 1
},
{} as {
a: 2
b?: 2
d?: 2
e: 2
f: 2
}
)
答案 0 :(得分:1)
简短的答案是,您不能对任意数量的值执行此操作。稍长一点的答案是您不应该尝试这样做,因为自然的递归定义{{3}}和可以诱骗编译器执行此操作的各种方法是will not work 。
如果您愿意支持最多一些合理但有限的参数,则可以。实际上,officially frowned upon当前只是路口的一些重载。尽管这个standard library definition of Object.assign()
,对于人们来说似乎运作良好。
假设我们以might change sometime中的Spread<L, R>
为起点,那么我们可以制作自己的SpreadTuple
,使其适用于固定长度的任何内容:
type Tail<L extends any[]> =
((...l: L) => void) extends ((h: infer H, ...t: infer T) => void) ? T : never;
type SpreadTuple<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple1<Tail<T>>>
type SpreadTuple1<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple2<Tail<T>>>
type SpreadTuple2<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple3<Tail<T>>>
type SpreadTuple3<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple4<Tail<T>>>
type SpreadTuple4<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple5<Tail<T>>>
type SpreadTuple5<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple6<Tail<T>>>
type SpreadTuple6<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple7<Tail<T>>>
type SpreadTuple7<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple8<Tail<T>>>
type SpreadTuple8<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTuple9<Tail<T>>>
type SpreadTuple9<T extends {}[], L extends number = T['length']> = L extends 0 ? never : L extends 1 ? T[0] : Spread<T[0], SpreadTupleX<Tail<T>>>
type SpreadTupleX<T extends {}[]> = T[number]; // give up
我这样做是为了让您看到如何轻松地将其扩展到您关心的任何长度。如果您愿意对许多Tail
进行硬编码,则可以不使用Spread<Spread<Spread<....>>>
来做到这一点。
无论如何,现在这有效:
// use default parameter R to expand result to easy-to-digest type
function merge<T extends object[], R = SpreadTuple<T>>(...sources: T): { [K in keyof R]: R[K] } {
return Object.assign({}, ...sources);
}
const result: Expected = merge(
{} as {
a: 1
b: 1
c?: 1
d?: 1
e?: 1
},
{} as {
a: 2
b?: 2
d?: 2
e: 2
f: 2
}
)
//const result: {
// c?: 1 | undefined;
// a: 2;
// e: 2;
// f: 2;
// b: 1 | 2;
// d: 1 | 2 | undefined;
//}
让我们尝试使用两个以上自变量的方法:
const r = merge({ a: 1, b: 2 }, { b: "3", c: "4" }, { c: true, d: false });
// { a: number; b: string; c: boolean; d: boolean; }
对我很好。
希望有帮助。祝你好运!
答案 1 :(得分:0)
type Result<T extends object[]> = UnionToIntersection<T[number]>
/**
* @author https://stackoverflow.com/users/2887218/jcalz
* @see https://stackoverflow.com/a/50375286/10325032
*/
type UnionToIntersection<Union> =
(Union extends any
? (argument: Union) => void
: never
) extends (argument: infer Intersection) => void
? Intersection
: never;
请参见{{3}}。