arrays - 打字稿说即使数组保证包含元素,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'.
}
解决方案
有关规范的答案,请参阅microsoft/TypeScript#30406。
对于这个问题,“为什么编译器认为即使我检查数组时它pop()
也可能返回”,简短的回答是“因为 TypeScript 标准库的方法调用签名返回”:undefined
length
pop()
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 length
of 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
}
问题是length
an 的属性Array<T>
是 type number
,并且没有“非零number
”类型可以缩小w.length
。为了支持这种事情,你需要一些不属于 TypeScript 的否定类型。有可能通过足够的编译器工作,有人可以为 TypeScript 中的数组提供足够的结构,以便对真实性检查w.length
将数组的类型缩小到您可以调用pop()
而不必担心undefined
退出的类型。
但这会给编译器和支持此用例的语言增加大量复杂性。收益不可能超过成本。
在没有这个的情况下,你可以更容易地跳过length
检查并调用pop()
,检查它是否undefined
存在。这对您来说是相同的工作量,因为它只是将检查从调用之前移到调用之后,它使编译器的事情变得更加简单。
此处发布的其他答案建议了此解决方法和其他解决方法,因此我不会深入探讨。我的主要观点是,该语言不具备允许length
检查影响pop()
. 或者,正如@RyanCavanaugh(TS 的开发负责人)在 microsoft/TypeScript#30406 中所说,
这不是我们能够追踪的
那好吧!
推荐阅读
- reactjs - React Native:更新对象内的数组
- c++ - avformat_close_input 内存泄漏?
- python - K 均值算法中 n_clusters 的最大值
- pytorch - 当我使用由 torch.nn.function.mse_loss 定义的损失函数时,损失将是 Nan
- android - RuntimeException:getParameters 失败(空参数)
- c# - 使用 CsvHelper 库,如何将空字段读取为 null 而不是空字符串?
- swift - 每当添加文档时更新视图 - Swift - Firebase Firestore
- java - 与 JAXB 在创建 XML 方面相比,Jackson 的性能如何?
- html - 增加两个部分之间的距离
- flutter - 如何从 Dart 中删除 if-else 链?