关闭 strictNullChecks 的 Typescript 索引访问

Typescript index access with strictNullChecks turned off

以下代码在启用 strictNullChecks 时有效。

interface IData {
  a: number;
  b: string;
}

const data: Partial<IData> = {};

// I am doing "as const" to make 'a' and 'b' string literal
// keys of IData. In the real code, these keys would come from elsewhere.
for (const key of ['a', 'b'] as const) {
  data[key] = undefined;
}

console.log(data);

flag关闭时报错:

Type 'any' is not assignable to type 'never'.(2322)

Playground

为什么?

有没有办法在不启用 strictNullChecks 或使用 any 的情况下克服这个问题?

也许你想要

interface IData {
  a: number;
  b: string;
}

const data: Partial<IData> = {};

for (const key of ['a', 'b']) {
  data[key] = null;
}

console.log(data);

Playground

但根据 Calz 的评论,不太清楚您的用例是什么

the --strictNullChecks compiler option is off, but I have not found an existing issue in GitHub about it. It is almost universally recommended 开启 --strictNullChecks 时,这似乎是 TypeScript 的限制,因此 [=120] 中的覆盖率几乎没有=] 以及有关关闭时的行为方式的文档。

我这里的分析是有几个东西交互不佳:

  • --strictNullChecks 关闭时,nullundefined 被认为可分配给 几乎 任何类型。这几乎使他们bottom types like the never type。但是,无论出于何种原因,它们 不能 分配给 never 类型。这是一个关键的区别(我可能认为这是一个错误或限制)。只有 never 可分配给 never:

    declare let never: never;
    declare let any: any;
    declare let v: never;
    v = undefined; // error!
    v = null; // error
    v = any; // error
    v = never; // okay
    
  • --strictNullChecks关闭时,nullundefined被吸收到unions(如never):

    type Foo = string | undefined;
    // type Foo = string
    
  • 当您尝试为联合类型的 key 赋值给 obj[key] 时,TypeScript 要求该值可赋给 intersection of the value types for each key in the union, as implemented in microsoft/TypeScript#30769

    interface Bar {
      x: number | string;
      y: string | boolean;
    }
    const bar: Bar = { x: 3, y: true };
    for (const key of ['x', 'y'] as const) {
      bar[key] = 1; // error! number is not assignable to string
      bar[key] = "okay"; // okay
    }
    

综合起来:

在此代码中:

for (const key of ['a', 'b'] as const) {
  data[key] = undefined;
}

因为 dataPartial<IData> 类型,--strictNullChecks 上,这个交集将是 (number | undefined) & (string | undefined),计算结果为只是 undefined。因此,当 key 仅已知为 "a" [=39 时,undefined 是您可以分配给 data[key] 的唯一安全值=].上面的赋值编译没有错误。

但是如果 --strictNullChecks 关闭 (number | undefined) & (string | undefined) 会崩溃为 number & string,也就是 never。并且 undefined 无论出于何种原因,都不能分配给 never。上面的作业给了你你所面临的错误。

废话。


那么,您可以如何进行?假设您不想打开 --strictNullChecks(这也是几乎普遍推荐的),并且您不想提交 issue in GitHub about it (which would probably end up being marked as "Working as Intended" or "Design Limitation", but maybe possibly would be marked as "Bug" which would be low on anyone's priority list to fix), then you can work around it via type assertion。您可以只说“undefined 可分配给 never 此处”,编译器将接受它:

for (const key of ['a', 'b'] as const) {
  data[key] = undefined as never; // okay
}

这种类型安全吗?好吧,当 --strictNullChecks 关闭时,它的类型安全性并不比 undefined 的任何其他处理低。类型断言本质上让你做不安全的事情,所以你 从编译器那里承担一些安全责任。但不幸的是,你在这里能做的也好不了多少。

(好吧,你知道的,除了打开 --strictNullChecks。还有所有 the --strict suite of compiler features, while you're at it. The --strict option is a "standard" level of type which is used in a lot of code bases; if you have a question about TS and --strict or one of its sub-features is not enabled, you will find fewer resources to help you. And the --strict suite is composed of features which have, in real world code bases, solved more problems than they've caused. Features which tend to make things worse for the average code base are kept out, like --noUncheckedIndexedAccess and --exactOptionalPropertyTypes)。

Playground link to code