typescript 有一种奇怪的行为,我无法解释。
我想实现的是这样一个函数:
function getDbRowFromRow<T, Key extends keyof T, R extends Pick<T, Key>>(
dbRow: T[],
key: Key,
row: R)
{
const map = new Map<T[Key], T>(dbRows.map(r => [r[key], r]));
// the key throws the type error here
map.get(row[key]);
}
https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAcwKZQCICMBKcDuAYgE5wC2e+APACoA0iA0qgJ6KoAeUqYAJgM6IA1qzjBE9RDnZceAxAAUYEIbQbMWAPk0AKAFCJEvXAX4AuCQG0AunQPDWFjXcOl8FnAEo9Ab3sQEfihEMgBDAAdEAF5EMFR8RABZCNpLDVsJXWNKfgA6MPCdYmjNREtiSxEWDOJrT08Abj17AHoWxCgAC1QHNi63QS6eqBZwntRiUmLu4lR7Aty0KCKCStY6poBfIA
抛出此错误:
Argument of type 'R[Key]' is not assignable to parameter of type 'T[Key]'.
Type 'R' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'R'.
现在我不太明白什么地方会被破坏,因为很明显,如果R扩展Pick<T,Key>,那么两个泛型对象的那部分一定是相同的。
另一个线索是,即使我尝试as T[Key]
行,它也会首先给我通常的强制转换为unknown
错误。因此,它认为这两种类型甚至没有一丁点的关联。
我错过了什么?有没有什么方法可以真正违反这种类型约束,或者这只是一个奇怪的打字脚本怪癖?
如果需要的话,我会把要解决的实际问题贴出来,我只是想一个最小的例子会更清楚。
2条答案
按热度按时间gab6jxml1#
ecbunoof2#
TypeScript编译器并不能对generic类型参数、它们的约束以及mapped和indexed access types的组合进行任意分析。它可以做到这一点,但只是在某种程度上。(请参阅microsoft/TypeScript#28884了解我所说的限制,尽管它不是100%与此相同。
你有
R extends Pick<T, K>
,因此我们可以把R
想象成类似于Pick<T, K> & U
的另一种类型U
。而且你希望编译器认识到(Pick<T, K> & U)[K]
可以赋值给T[K]
。这几乎肯定是真的(我想不出它可能是假的),但编译器看不到它。另一方面,它 * 可以 * 看到Pick<T, K>[K]
可赋值给T[K]
。令人困惑的是,R
是Pick<T, K>
的任意子类型。这意味着我们可以通过将
R
类型的值扩展到Pick<T, K>
类型,这是类型安全的(就像TypeScript中的任何东西一样)来解决这个问题:Playground链接到代码