当您调用Object.values(v)
时,对于它推断的generic类型参数T
,编译器try 将v
与{[k: string]: T}
匹配(根据this call signature).
因此,如果v
属于类似{[k: string]: Z}
的类型,则编译器会将T
推断为Z
,所有操作都将成功.但如果v
‘S的类型是像{[k: string]: X} | {[k: string]: Y}
这样的union张唱片,情况就不同了.人们可能希望T
可以被推断为联盟X | Y
.但推理算法并不是以这种方式工作的.类型X | Y
不会直接出现在输入类型中的任何位置,并且编译器不会synthesize个来自多个推理候选者的联合类型.取而代之的是,编译器 Select 了一个候选者.假设是X
...然后,如果Record<string, X> | Record<string, Y>
不可分配给Record<string, X>
,则发出错误.
拒绝合成联合类型是故意的;人们通常宁愿看到错误,也不愿让编译器开始创建联合以使程序成功,特别是在许多情况下,这会使几乎每个调用都成功,即使是做像compare(3, "oops")
这样疯狂的事情的调用,假设compare
是像<T>(a: T, b: T) => number
这样的类型.人们want看不到这一点,而不是让T
被推断为number | string
.
在你的情况下,你want的结合是可以推断的.在microsoft/TypeScript#44312处有一个开放的特征请求,以具有一种方式来说明应该发生这样的推断.但它还不是语言的一部分,即使它是语言的一部分,Object.values()
调用签名也可能不会被更改以使用它.
因此,我们不能依赖编译器直接推断您想要的联合类型.
取而代之的是,当我们调用Object.values()
时,我们可以specify并集类型.这看起来像是Object.values<X | Y>(v)
.在您的示例中,您可以只编写Object.values<number | boolean>(value)
,然后就完成了.
但您不希望以这种方式硬编码该类型参数.您希望它依赖于value
,这样,如果它的类型更改为某个其他记录的联合,它将继续工作.这意味着:给定一个像{[k: string]: X} | {[k: string]: Y}
这样的类型的值value
,我们如何才能将类型X | Y
变为compute呢?
在这里,我们可以对value
使用the typeof
type query operator来获得值的类型,然后我们可以用string
来对该类型进行index into,以获得value
在string
键处的属性的类型.
即:
Object.values<typeof value[string]>(value); // okay
b >
或者,查看一般示例:
function foo<X, Y>() {
type A = Record<string, X>;
type B = Record<string, Y>;
function func(value: A | B) {
return Object.values<typeof value[string]>(value);
}
// func(value: Record<string, X> | Record<string, Y>): (X | Y)[]
}
在foo()
内部,func()
函数接受类型A | B
的值,并返回类型(X | Y)[]
的值,而无需我们自己硬编码类型X | Y
.取而代之的是,typeof value[string]
的计算结果是X | Y
,我们使用它.
Playground link to code个