类型“this[K]”不可分配给类型“NonNullable<this[K]>”

时间:2021-03-25 20:15:36

标签: typescript typescript-generics

我不明白为什么打字稿会为此抛出错误:

public saveGet<K extends keyof this> (key: K, def: NonNullable<this[K]>): NonNullable<this[K]> {
    if (this[key] !== null && this[key] !== undefined) {
      return this[key]
    } else {
      return def
    }
  }

错误:

Type 'this[K]' is not assignable to type 'NonNullable<this[K]>'.
  Type 'this[keyof this]' is not assignable to type 'NonNullable<this[K]>'.
    Type 'this[string] | this[number] | this[symbol]' is not assignable to type 'NonNullable<this[K]>'.
      Type 'this[string]' is not assignable to type 'NonNullable<this[K]>'.ts(2322)

据我所知,打字稿编译器应该能够从 null 中排除 undefinedthis[key](因此进行比较),因此我应该能够返回它。

谁能解释一下? 如果这已经被问到,我很抱歉。

1 个答案:

答案 0 :(得分:3)

这里的主要问题是 TypeScript 编译器没有执行那种高阶推理,以查看何时可以将值分配给依赖于尚未指定的 {{3} } 类型参数。在您的示例代码中,您希望编译器使用 conditional type 来意识到 this[key] 应该从 this[K] 缩小到 NonNullable<this[K]>。不幸的是,KgenericsaveGet() 的主体内都未指定。 (K 显然是一个泛型参数,但 this 是一种“虚拟”泛型参数。您不必声明它,但它的作用就像一个未在类体内指定的参数并在您拥有类的实例时指定。)因此编译器不知道如何缩小 this[key] 的类型。关于此问题的规范未决问题是 control flow analysis,它讨论了尝试解决此问题所涉及的一般挑战。

这里的解决方法可能是编写一个 polymorphic this type 来控制缩小行为。


一个不太重要但仍然相关的问题是,当您使用括号索引访问时,编译器不会对对象属性进行基于控制流的缩小;有关详细信息,请参阅 microsoft/TypeScript#33912。因此,遗憾的是检查 this[key] 不会对 this[key] 的后续检查做任何事情。此处的解决方法是将 this[key] 分配给它自己的变量并检查它。


这意味着您的问题的当前解决方法如下所示:

// user defined type guard function
function isNonNullable<T>(x: T): x is NonNullable<T> {
  return x !== null && x !== undefined
}

public saveGet<K extends keyof this>(
  key: K, def: NonNullable<this[K]>): NonNullable<this[K]> {
  const thisKey = this[key]; // save into own variable
  if (isNonNullable(thisKey)) {
    return thisKey; // okay now
  } else {
    return def
  }
}

user-defined type guard function