从空数组访问对象属性时,如何使TypeScript编译失败

时间:2018-08-10 00:33:15

标签: typescript

如果TypeScript可以保护我免受空数组的侵扰,我会很乐意。

我已经看到很多类似这样的运行时错误

const emptyArr: Array<{ prop: string }> = []
causesError = emptyArr[0].prop
  

TypeError:无法读取未定义的属性'foo'

编码人员可能想做:

const emptyArr: Array<{ prop: string }> = []
if (emptyArr.length > 0) {
   noMoreError = emptyArr[0].prop
}

我认为TypeScript应该能够推断一个数组可能为空,并且能够对此发出警告。

我玩了以下类似的东西,但仍然无法使它起作用:

interface PossiblyEmptyArray<T> extends Array<T> {
  [i: number]: T | undefined;
}
const arr: PossiblyEmptyArray<{ prop: string }> = []
arr[0].prop // fails at runtime still

我相信TypeScript 2.x和3.0之间的这种行为是一致的

实际上我确实想要 工作 而没有extends Array位。 但这是一个编译时错误,应为:

interface PossiblyEmptyArray<T> {
  [i: number]: T | undefined;
  length: number
}
const arr: PossiblyEmptyArray<{ prop: string }> = []
if (arr.length > 0) {
  arr[0].prop // no "prop" on string | undefined
}

此类型的结果相同:

type PossiblyEmptyArray<T> = Array<T> | void[]

编辑:

我对这个问题看得越多,我认为TS似乎无法基于 longityness 检查来缩小Array<T> | void[]或类似类型,这可能是我想要的东西的必要条件。

2 个答案:

答案 0 :(得分:1)

如果要在编译时保护自己免受空数组的侵害,可以在Typescript 3中定义一个泛型类型,该泛型类型要求在第一个索引处有一个项目,随后需要多个项目。例如:

type NonEmptyArray<T> = [T, ...Array<T>];
const notValid: NonEmptyArray<number> = []; // compilation error!
const valid1: NonEmptyArray<number> = [1];
const valid2: NonEmptyArray<number> = [1, 1, 2, 3, 5, 8];

请注意,由于类型定义中的其余(...)表达式,因此这仅在打字稿3中有效。

答案 1 :(得分:1)

您可以使用用户定义的类型防护:

type PossiblyEmptyArray<T> = (T | undefined)[];
type NonemptyArray<T> = [T, ...(T | undefined)[]];

function isNonempty<T>(arr: PossiblyEmptyArray<T>): arr is NonemptyArray<T> {
  return arr.length > 0;
}

const arr: PossiblyEmptyArray<{ prop: string }> = []
if (isNonempty(arr)) {
  arr[0].prop
}

但是,这种方法除了检查固定数量的元素外,不会泛泛化,除非可以想象的是,如果您使用精美的品牌类型。 TypeScript团队considered and decided against支持通常检查与数组长度有关的检查。