真实的的例子是,我想实现一个函数,通过输入一个对象数组来返回一个包含输入数组中所有属性的对象。
function unit<T extends object>(arr: T[]) {
let obj = {};
arr.forEach((val) => {
obj = { ...obj, ...val };
});
return obj;
}
const a = { name: "l" };
const b = { age: 1 };
const person = unit([a, b]);
我希望person是一个基于输入的动态类型脚本值,但是我不知道如何实现它。
2条答案
按热度按时间7cwmlq891#
TypeScript不会对循环中的类型变化进行建模,因此编译器不可能计算出
obj
的结果类型,您必须显式地告诉它您期望的类型。所以,我们期望什么?好吧,我提议
应该导致
person
是a
和b
类型的交集:注意,如果
arr
的不同元素有冲突的属性类型,这就不正确了。如果你写unit([{a: 0}, {a: ""}])
,结果值就不是{a: number} & {a: string}
类型。它并不与它们相交。尽管如此,这已经足够接近了,而且当你把泛型的东西扩展到一起时,编译器通常会这样做:这就是我们的目标:产生
arr
的每个元素的类型的交集。首先,您应该在
arr
的元组类型中创建unit()
函数generic,如下所示:(其中
[...T]
是一个可变元组类型,它向编译器提供提示,如果可能,我们希望T
是元组类型)否则,如果在
arr
的 * element types * 中将其保留为泛型,那么类型参数T
将最终成为每个元素类型的并集,这很难分解为它的组成部分,例如,检查person
和notPerson
之间的差异:在这两种情况下,在您的版本中,
T
都将被推断为{name: string} | {age: number}
。但是person
应该是{name: string} & {age: number}
类型,而notPerson
应该是{name: string} | {age: number}
类型。为了保持它们的独立性,我们不仅需要关注所有元素类型的并集,还需要关注每个单独的元素类型。好的,那么,如果
T
是一个元组,我们如何将它转换成它的元素类型的交集呢?它使用条件类型推断从逆变类型位置将并集转换为交集,技术与Transform union type to intersection type的工作方式类似。
我们可以Assert
obj
的类型为TupleToIntersection<T>
:但是如果我们这样做的话,编译器会忘记
obj
是类对象的,当你扩展它的时候会很生气,所以让我们使用Extract<T, U>
实用程序类型来提醒它:这与
TupleToIntersection<T>
相同,只要它确实是类对象的而不是原语的。好吧,我们来测试一下:
看起来不错!
Playground代码链接
hiz5n14c2#
这就是你要找的吗?