使用括号表示法访问对象 属性 时键入缩窄符

Type narrowing breaks when accessing an object property with bracket notation

为什么测试 1 和 2 在这里工作,但测试 3 在 foo[barConst]++: 'Object is possibly "undefined".' 处显示编译器错误?我经常需要通过括号表示法访问属性,因此喜欢为这些属性设置常量,但 TypeScript 不允许这样做。它也不适用于 const enums。这是错误还是有充分的错误原因?

const barConst = 'bar';

interface Foo {
    [barConst]?: number;
}

function test1(foo?: Foo) {
    if (foo && foo.bar) {
        foo.bar++;
    }
}

function test2(foo?: Foo) {
    if (foo && foo['bar']) {
        foo['bar']++;
    }
}

function test3(foo?: Foo) {
    if (foo && foo[barConst]) {
        foo[barConst]++; // compiler error: 'Object is possibly "undefined".'
    }
}

Playground

继续评论讨论。 (我仍然不知道你在找什么) 你可以尝试这样的事情:

type barConst = 'bar';

type Foo<K extends string> = Record<K, number>;

function test3(foo?: Foo<barConst>) {
  if (typeof foo !== 'undefined') {
    foo['bar']++;
  }
}
let x: Foo<barConst> = { bar: 2 };

test3(x);
console.log(x);

您可以自动完成并且没有编译器错误。 不过,请更新您的问题。

通过计算的 propertyNames/literal 表达式缩小 属性 访问范围目前似乎是不可能的。看看这个 issue and its PR, also that issue.

您可以使用字符串文字缩小 属性 访问范围,例如 foo["bar"]foo[barConst] 等动态表达式不起作用。将 foo[barConst] 分配给一个变量,然后将 working/narrowing 分配给该变量是一种替代方法,但需要额外的声明。

在您的情况下,最简单的解决方案是使用非空断言运算符 ! 强制转换表达式。当您对虚假值进行预检查时,您在这里是安全的:

function test3(foo?: Foo) {
    if (foo && foo[barConst]) {
        foo[barConst]!++; 
    }
}

随着 TypeScript 4.7 的发布,由于它改进了括号元素访问的控制流分析,原始代码不再导致编译器错误,因为 foo && foo[barConst] 守卫现在正确地缩小了foo[barConst] 访问类型为 number

有关 CFA 总体更新的更多详细信息,请参阅 PR #40617

4.7 Playground