打字稿:当键存储在变量中时,类型缩小不适用于“in”

Typescript: type narrowing not working for `in` when key is stored in a variable

考虑这个简单的问题 snippet。我也贴在这里:

type A =
  | {
      b: number;
    }
  | {
      c: number;
    };

function f1(a: A) {
  if ('b' in a) {
    return a['b']; // No problem!
  }
  return 42;
}

function f2(a: A) {
  const key = 'b';
  if (key in a) {
    return a[key];  // Property 'b' does not exist on type 'A'
  }
  return 42;
}

为什么 a 的类型在 f2 中没有缩小到 {b: number}? (因为它是 f1

这与 microsoft/TypeScript#10530; type narrowing from control flow analysis only happens for properties that are directly literals like "b" and not for arbitrary expressions whose types are literal types 本质上是同一个问题。 Issue #10530 讨论了通过 属性 访问缩小范围...比如 a.ba["b"] 确实 导致 a 缩小, vs a[key], .

如您所见,in 运算符类型保护(在 microsoft/TypeScript#15256 中实现)也会发生这种情况,其中 "b" in a 缩小了 a 的类型, 但 key in a 没有。这在#10530(pre-dates in 类型保护)中没有明确提及,但我认为没有另一个现存的问题专门针对此。

根据 microsoft/TypeScript#10565,最初尝试解决 #10530 中的问题,为文字类型的任意表达式添加类型保护功能会显着降低编译器性能。也许对所有 x in y 检查执行额外分析比对所有 y[x] 属性 访问执行额外分析更便宜,但至少到目前为止没有人关心太多。

你总是可以在 GitHub 中打开你自己的问题(很多问题最终都是重复的,我不是 100% 确定这不会被认为是 #10530 的重复,或者没有其他问题重复),但实际上它可能不会很快改变。


如果您想要解决不能仅用字符串文字替换 key 的情况,您可以编写自己的 user-defined type guard function,称为 hasProp(obj, prop)。该实现只是 return prop in obj,但其类型签名明确指出 true 结果应导致 obj 缩小到仅具有键类型 prop:

function hasProp<T extends object, K extends PropertyKey>(
    obj: T, prop: K
): obj is Extract<T, { [P in K]?: any }> {
    return prop in obj;
}

然后在您的函数中,将 key in a 替换为 hasProp(a, key):

function f3(a: A) {
    const key = 'b';
    if (hasProp(a, key)) {
        return a[key];  // okay
    }
    return 42;
}

Playground link to code