为什么 <array>.[n] 类型不同于 <array>.at(n)

Why <array>.[n] type are different from <array>.at(n)

我正在尝试从使用 <array>.at() 切换到 <array>.[] 以保持一致性。但这是我 运行 遇到的一个问题:

const array: string[] = [];

if (array[0]) {
  const item = array[0]; // string
}

if (array.at(0)) {
  const item = array.at(0); // string | undefined
}

if (array[0]) {
  const item = array.at(0); // string | undefined
}

为什么类型不同?我错过了什么?我已经阅读了有关 at() 方法的信息,但是关于 typescript 的信息并不多。

typescript@4.6.4

有了那些 if (array[0]) 风格的类型保护(特别是因为你现在说你启用了 noUncheckedIndexedAccess 选项),可以说答案只是 TypeScript 不会缩小类型函数调用。毕竟,在一般情况下,函数可以在每次调用时 return 不同的值(并非所有函数都是 )。

你可以用 const:

锁定它
// Note: `noUncheckedIndexedAccess` enabled

const array: string[] = [];

if (array[0]) {
    const item = array[0]; 
    //    ^? −−−− string
    console.log(array[0]); // string
}

const item1 = array.at(0);
if (item1) {
    console.log(item1);
    //          ^? −−−− string
}

Playground link


但这提出了一个有趣的问题,即为什么 没有 守卫是不同的(如果你 没有 noUncheckedIndexedAccess 已启用)?

const array: string[] = [];

const i1 = array[0];
//    ^? −−−− string
const i2 = array.at(0);
//    ^? −−−− string | undefined

Playground link

我怀疑原因有两个:

  1. 很久以前,TypeScript 团队做出了务实的决定,即(默认情况下¹)索引访问数组(如 string[])应该 return 数组的元素类型( string),而不是数组的元素类型或 undefined (string | undefined),尽管后者是对 property access 的更正确解释(这才是真正的索引访问)以及您确实可以从中获得 undefined 的运行时现实。想象一下,如果没有它们,TypeScript 代码会有多笨拙! Every 数组访问将涉及一个 | undefined,它最终会用 non-null 断言或其他任何东西乱扔代码。所以他们采取了务实的方法。

  2. at method 的范围比一般的索引访问要小得多——它根本不打算在任何地方取代索引访问,它的主要用例是索引为负数时。所以它的类型反映了它可能 return undefined 的事实,因为 不会 到处都需要 non-null 断言——只有在那些地方您使用 at 而不是索引访问。因此,没有理由不符合规范,在这种情况下,类型与规范保持一致更有用。


¹ 关于“默认”:您 可以 告诉 TypeScript 您希望索引访问通过 noUncheckedIndexedAccess option 包含 | undefined。启用该选项后,我上面的示例变为:

// With `noUncheckedIndexedAccess` enabled
const array: string[] = [];

const i1 = array[0];
//    ^? −−−− string | undefined        <== Changed
const i2 = array.at(0);
//    ^? −−−− string | undefined

Playground link