We can convert unions into intersections (感谢 @jcalz!),我们可以很好地对可变参数进行建模,所以我想尝试用一个重载来建模 Object.assign
。以下是我得出的结果:
// Maps elements of a tuple to contravariant inference sites.
type MapContravariant<T> = {
[K in keyof T]: (x: T[K]) => void
}
type TupletoIntersection<T, Temp = MapContravariant<T>> =
// Ensure we can index with a number.
Temp extends Record<number, unknown>
// Infer from every element now.
? Temp[number] extends (x: infer U) => unknown ? U : never
: never;
declare function assign<T extends object, Rest extends object[]>(
x: T,
...xs: Rest
): T & TupletoIntersection<Rest>;
不幸的是,这在以下情况下给出的结果并不完全正确:
let asdf = assign({x: "hello"}, Math.random() ? {x: "hello"} : {z: true });
目前,asdf
的类型是:
| ({ x: string; } & { x: string; z?: undefined; })
| ({ x: string; } & { z: boolean; x?: undefined; })
多么美丽的类型啊!不幸的是,你可以看到联合体的第二个元素试图将具有冲突属性的类型相交,对于 x
这是没有意义的。
此外,@weswigham 指出,这对于最初只是一个普通数组的情况并不适用,该数组的元素最终被扩展到 Object.assign
中。
我不禁想知道我们是否能在这里做得更好。
5条答案
按热度按时间dtcbnfnu1#
asdf
的疯狂类型与你目前从Object.assign()
获得的相同,对吗?如果目标是替换各种数量的硬编码交集的重载,我认为像上面的那样可以实现。还是目标是用比包交集更适用的返回类型替换
Object.assign()
的返回类型(例如Spread<L, R>
的一些泛化)?gmxoilav2#
在几个TS版本的顶峰之后,我认为我找到了一种在我能想到的情况下导致正确答案的打字方式:
类型
被正确地推断为
{ x: string, z?: undefined } | { x: string | undefined, z: boolean }
Playground链接,它也有类型测试: https://tsplay.dev/4w1LyW
我确实想知道我们是否能在这里做些什么更好。
@DanielRosenwasser,这是否算作某种“更好”的定义?
xsuvu9jc3#
@lazytype 真有趣,我们竟然同时想到了这个!这是我对可变参数Object.assign类型的实现,它简单地创建了传入的所有类型之间的交集——就像所有重载函数手动所做的那样。
这里是查看它的行动方式的链接! playground link
请注意,这个例子的简单性在某种程度上导致它在底部的Math.random示例中出现问题,以至于我现在还不太理解。
当然很乐意看到这样的东西被添加进来!我遇到了这个问题,即非人为的方式多次将可变参数Object.assign调用类型化为
any
,所以说这已经困扰我一段时间了。ukqbszuj4#
请注意,此示例的简单性在某种程度上导致它在底部的 Math.random 示例中出现问题,以至于我现在无法100%理解。
@tvler 由于
Math.random() ? {x: "hello"} : {z: true}
的类型为{ x: string; z?: undefined } | { z: boolean; x?: undefined }
,当它尝试应用UnionToIntersection
时,会得到{ x: string; z?: undefined } & { z: boolean; x?: undefined }
,而{ x: string; z?: undefined } & { z: boolean; x?: undefined }
只能是never
。基本上,当
assign
的参数具有重叠属性时,联合到交集的方法总是会出现这种问题。要处理这种情况,需要让参数列表中后面的属性替换前面的属性,而不是与它们相交。我在提案中定义的辅助类型AssignProperties
试图做到这一点。iyr7buue5#
好的,谢谢你的解释!就像我说的,我直到自己发现这个问题之前都没有看到你的工作,很高兴看到其他人对此有更多的思考。