typescript匹配一个特定的键,使其为两个可能不同的泛型对象的相同值

dddzy1tm  于 2023-06-24  发布在  TypeScript
关注(0)|答案(2)|浏览(150)

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错误。因此,它认为这两种类型甚至没有一丁点的关联。
我错过了什么?有没有什么方法可以真正违反这种类型约束,或者这只是一个奇怪的打字脚本怪癖?
如果需要的话,我会把要解决的实际问题贴出来,我只是想一个最小的例子会更清楚。

gab6jxml

gab6jxml1#

function getDbRowFromRow<T, Key extends keyof T> (
  dbRows: T[],
  key: Key,
  row: Pick<T, Key>) // <- you don't need the whole new generic arg here
{
  const map /* : Map<T[Key], T> */ = new Map(dbRows.map(r => [r[key], r]));
  map.get(row[key]);
}
ecbunoof

ecbunoof2#

TypeScript编译器并不能对generic类型参数、它们的约束以及mappedindexed 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]。令人困惑的是,RPick<T, K>的任意子类型。
这意味着我们可以通过将R类型的值扩展到Pick<T, K>类型,这是类型安全的(就像TypeScript中的任何东西一样)来解决这个问题:

function getDbRowFromRow<T, K extends keyof T, R extends Pick<T, K>>(
  dbRows: T[], key: K, row: R) {
  const map = new Map<T[K], T>(dbRows.map(r => [r[key], r]));
  const r: Pick<T, K> = row; // okay
  map.get(r[key]); // okay
}

Playground链接到代码

相关问题