首页 > 解决方案 > 打字稿说即使数组保证包含元素,array.pop() 也可能返回 undefined

问题描述

为什么 Typescript 会警告我,即使在我验证了我正在从非空数组中删除一个元素之后它pop()也可能返回?undefined

type Person = {
  name: string;
};

const people: Person[] = [{ name: 'Sandra' }];

if (people.length) {
  const { name } = people.pop();
  // TS Error: Property 'name' does not exist on type 'Person | undefined'.
}

即使我使保证更明确,我也会得到同样的错误:

if (people.length > 0 && people[people.length - 1] !== undefined){
  const { name } = people.pop();
  // TS Error: Property 'name' does not exist on type 'Person | undefined'.
}

标签: arraystypescript

解决方案


有关规范的答案,请参阅microsoft/TypeScript#30406


对于这个问题,“为什么编译器认为即使我检查数组时它pop()也可能返回”,简短的回答是“因为 TypeScript 标准库的方法调用签名返回”:undefinedlengthpop()Array<T>T | undefined

interface Array<T> {
  // ... elided
  pop(): T | undefined;
  // ... elided
}

因此,每当您调用pop()数组时,返回值的类型将包括undefined.


下一个合乎逻辑的问题是“为什么他们不做出更好的调用签名,T如果length非零并且undefined如果length是零,则返回?” 答案“因为检查length一般数组类型的属性不会改变数组的明显类型,所以调用签名无法区分”。

例如,您可以像这样添加一些调用签名:

interface Array<T> {
  pop(this: { length: 0 }): undefined;
  pop(this: { 0: T }): T;
}

通过使用this参数,每个调用签名只有在调用方法的数组与指定类型匹配时才会被选中。如果数组有 a lengthof 0,则返回undefined。如果数组T在键处有一个类型的元素0,则返回T

这适用于长度固定且元素索引已知的元组类型:

declare const u: [];
const a = u.pop(); // undefined

declare const v: [1, 2];
const b = v.pop(); // 1 | 2

但是当然调用pop()元组是一个坏主意,而不是你可能想要的那种东西。如果您有一个可变长度数组并对其进行调用pop(),则不会选择任何调用签名并且您会退回到内置的T | undefined,即使您尝试检查length

const w = Math.random() < 0.5 ? [] : ["a"] // string[]
if (w.length) {
  w.pop().toUpperCase(); // error! Object is possibly undefined
}

问题是lengthan 的属性Array<T>是 type number,并且没有“非零number”类型可以缩小w.length。为了支持这种事情,你需要一些不属于 TypeScript 的否定类型。有可能通过足够的编译器工作,有人可以为 TypeScript 中的数组提供足够的结构,以便对真实性检查w.length将数组的类型缩小到您可以调用pop()而不必担心undefined退出的类型。

但这会给编译器和支持此用例的语言增加大量复杂性。收益不可能超过成本。


在没有这个的情况下,你可以更容易地跳过length检查并调用pop(),检查它是否undefined存在。这对您来说是相同的工作量,因为它只是将检查从调用之前移到调用之后,它使编译器的事情变得更加简单。

此处发布的其他答案建议了此解决方法和其他解决方法,因此我不会深入探讨。我的主要观点是,该语言不具备允许length检查影响pop(). 或者,正如@RyanCavanaugh(TS 的开发负责人)在 microsoft/TypeScript#30406 中所说

这不是我们能够追踪的

那好吧!

Playground 代码链接


推荐阅读