TypeScript 中 Object is possibly ‘null’ or ‘undefined’ 错误
当我们尝试访问可能具有 null 或 undefined 值的对象的属性时,会发生“Object is possibly ‘null’ or ‘undefined’ ”错误。 要解决该错误,需要在引用为空时使用可选链接进行短路,例如 person?.address?.country
。
下面是产生上述错误的示例代码
type Person = {
address?: { // 👈️ may be null or undefined
country?: string;
city?: string;
} | null;
};
const person: Person = {};
// ⛔️ Error: Object is possibly 'null' or 'undefined'.
console.log(person.address.country);
Person
类型的地址属性标记为可选,也可以为空。
这就是我们无法安全地访问对象的 country 或 city 属性的原因。
为了解决这个问题,我们可以使用可选的链接 ?.
运算符。
type Person = {
address?: {
country?: string;
city?: string;
} | null;
};
const person: Person = {};
// 👇️ const result: string | undefined
const result = person?.address?.city;
问号点
?.
语法在 TypeScript 中称为可选链接,类似于使用点表示法访问对象的嵌套属性,但如果引用为空(null 或 undefined),它不会导致错误,而是简写。
这种方法通常用于从远程 API 获取数据或从文件中读取数据,其中某些属性可能没有值。
另一种方法是使用简单的 if 语句作为类型保护。
type Person = {
address?: {
country?: string;
city?: string;
} | null;
};
const person: Person = {};
// 👇️ checks for both null and undefined
if (person.address != null) {
console.log(person.address.country);
console.log(person.address.city);
}
我们使用 if 语句来检查 person.address
属性是否不等于 null 或 undefined。
一旦我们进入 if 块,TypeScript 就知道
address
属性是一个对象,而不是null
或undefined
。
请注意
,我们使用了松散不等于!=
,它同时检查 null 和 undefined。
松散比较涵盖 null 和 undefined,因为在松散比较中,null 等于 undefined。
console.log(null == undefined); // 👉️ true
console.log(null === undefined); // 👉️ false
如果我们确定该属性不能具有 null 或 undefined 值,则可以使用非空断言运算符。
type Person = {
address?: {
country?: string;
city?: string;
} | null;
};
const person: Person = {
address: {
country: 'Austria',
city: 'Linz',
},
};
// 👇️ non-null assertion operator
console.log(person.address!.city); // 👉️ "Austria"
console.log(person.address!.country); // 👉️ "Linz"
感叹号是 TypeScript 中的非空断言运算符。
它在不进行任何显式类型检查的情况下从类型中删除
null
和undefined
。
当我们使用这种方法时,基本上是在告诉 TypeScript 这个值永远不会为 null 或 undefined。
我们在 address 属性之后使用它,所以我们告诉 TypeScript person.address
永远不会有 null 或 undefined 的值。
如果我们在 if 语句中进行比较,请使用逻辑与 &&
运算符来确保属性的类型正确。
type Person = {
address?: {
country?: string;
city?: string;
streetNumber?: number;
} | null;
};
const person: Person = {};
if (
person?.address &&
typeof person.address.streetNumber === 'number' &&
person.address.streetNumber > 5
) {
console.log('success');
}
逻辑 AND
&&
运算符确保address
属性不为 null 或 undefined,并且streetNumber
属性存在于对象上并且是一个数字,然后再将其与数字 5 进行比较。
这是必需的,因为如果引用为空(null 或 undefined),可选的链接运算符 ?.
将返回 undefined 并且 TypeScript 不允许我们将 undefined 与数字进行比较。
例如,这会失败:
type Person = {
address?: {
country?: string;
city?: string;
streetNumber?: number;
} | null;
};
const person: Person = {};
// ⛔️ cannot compare possibly undefined to number
if (person?.address?.streetNumber > 5) {
console.log('success');
}
结果可能具有未定义的值,因为这是可选链接运算符
?.
短路时的返回值。
streetNumber
属性的值可能为 undefined,因此我们不能直接将它与数字进行比较。
避免出现“ Object is possibly ‘null’ or ‘undefined’ ”错误的另一种常见方法是使用逻辑与 &&
运算符。
type Person = {
address?: {
country?: string;
city?: string;
} | null;
};
const person: Person = {};
if (person.address && person.address.city) {
// 👇️ const result: string
const result = person.address.city;
}
如果左边的值是假的(例如
undefined
),逻辑 AND&&
运算符将不会计算右边的值。
如果 person.address
返回 undefined,那么 person.address.city
根本不会被评估,所以我们不会得到错误。
if 条件中的所有值都必须为真,if 块才能运行。
真值是所有非假值。
JavaScript 中的假值是:null、undefined、false、0、””(空字符串)、NaN(不是数字)。
这就是为什么 TypeScript 能够在 if 块中推断结果变量的类型为字符串。
在这种情况下,解决“ Object is possibly ‘null’ or ‘undefined’ ”错误的更好方法是使用 typeof
运算符。
type Person = {
address?: {
country?: string;
city?: string;
} | null;
};
const person: Person = {};
if (person.address && typeof person.address.city === 'string') {
// 👇️ const result: string
const result = person.address.city;
}
我们明确检查 city
属性的类型是否为字符串。 这比检查值是否为真要好,因为空字符串在 JavaScript(和 TypeScript)中是假值。
这是一个示例,说明为什么使用 typeof
更好。
type Person = {
address?: {
country?: string;
city?: string;
} | null;
};
const person: Person = { address: { city: '' } };
if (person.address && person.address.city) {
// 👉️ does NOT run
console.log('⛔️ this does NOT run');
} else {
// 👉️ else block runs
console.log('✅ this runs');
}
else
块在上面的示例中运行。
city
属性指向一个空字符串(虚假值),因此在我们的场景中仅检查该值是否为真可能还不够。
尽可能明确并使用 typeof
运算符总是更好,因为它可以帮助我们避免一些难以发现的错误。