Typescript 3.5 中的索引访问

dom*_*nik 0 typescript

我想编写一个类型安全的 getter,它也可能返回一些修改后的值(但我认为仍然是合理的)。

class Foo {
  a: number;
  b: boolean;
}

function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] {
  switch (p) {
    case 'a':
      return 1;
    case 'b':
      return o[p];
  }
  return undefined;
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我想getProp返回Foo[K]以便 inconst v = getProp(f, 'a') v是类型number。但是,代码在我返回的地方抛出一个错误,1因为它不能分配给never.

请注意,这在 Typescript 3.4 中有效,因为“修复了对索引访问类型的不健全写入”(https://devblogs.microsoft.com/typescript/annoucing-typescript-3-5/)。

我如何最好地编写此代码但不编写return 1 as any

jca*_*alz 6

是的,自 TypeScript 3.5 发布以来,此问题已被频繁报告。正如您所指出的,索引访问改进健全性是一个突破性的变化,它捕获了许多实际的错误,但不幸的是,也警告了相当安全的代码,例如您正在做的事情。

在这一点上,潜在的问题是扩展联合的泛型类型参数不会通过控制流分析变窄(请参阅microsoft/TypeScript#24085)。编译器没有办法说,既然p可以缩小到"a",那么类型参数K应该缩小到"a"。一般来说它不应该被允许这样做,因为K可能永远是"a" | "b"。该语言目前缺少一些允许安全发生的功能(例如,如果K可以说 not toextends "a" | "b"但相反,它必须是or but not ,那会有所帮助)。extends_oneof "a" | "b""a""b""a" | "b"


现在有解决方法和重构。@TitianCernicova-Dragomir 给出答案是一种可能的解决方法,使用单调用签名重载函数从本质上将实现内部的行为恢复到 TS3.4 及更低版本中完成的检查。是的,它不安全;这就是 TS3.5 修复的内容。其他涉及类型断言或其他不良行为的变通方法也应该有效。

我想添加这个答案的唯一原因(除了提供上面的链接)是提到一个可以帮助的重构,这取决于你的用例。这个想法是完全忘记控制流变窄,因为编译器不能为泛型做到这一点。相反,您应该返回一些编译器可以识别为合法的东西Foo[K],方法是使用 typeFoo键对 type对象进行索引K。毕竟,索引一个对象是,如果你眯着眼睛看它,就像switch在键上做一个:

function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] {
  return { a: 1, b: o.b }[p];
}
Run Code Online (Sandbox Code Playgroud)

代码链接