Typescript:当密钥存储在变量中时,类型缩小不适用于“in”

LGe*_*lis 7 typescript type-narrowing

考虑这个简单的片段。我也把它粘贴在这里:

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;
}
Run Code Online (Sandbox Code Playgroud)

为什么 of 的类型没有a缩小为{b: number}in f2?(因为它是为了f1

jca*_*alz 10

这本质上是与microsoft/TypeScript#10530相同的问题;控制流分析的类型缩小仅发生在直接像文字一样的属性上"b",而不发生在类型是文字类型的任意表达式上。Issue #10530 讨论通过属性访问缩小范围...例如a.bor a["b"],它确实会导致a缩小,而 vsa[key]不会

正如您所注意到的,这种情况也发生在in运算符类型保护(如在microsoft/TypeScript#15256中实现)中,其中"b" in a缩小了 的类型a,但key in a没有。#10530(早于类型in保护)中没有明确提到这一点,但我认为没有另一个专门与此相关的现有问题。

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

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


如果您想要一种解决方法来解决不能仅替换key为字符串文字的情况,您可以编写自己的用户定义类型保护函数,称为hasProp(obj, prop). 该实现只会返回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;
}
Run Code Online (Sandbox Code Playgroud)

然后在您的函数中替换key in ahasProp(a, key)

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

Playground 代码链接