引言:困扰新手的问题
\n你是否曾经在编写 TypeScript 时遇到过类似 Cannot read property 'name' of undefined 的错误?很熟悉吧,对吧?尤其是在处理来自服务器的数据或深度嵌套的对象时。正是在这些时刻,你会问:“TypeScript 中是否有一个运算符可以安全地访问属性,而不用担心程序崩溃?”
在 Stack Overflow 上,这个问题已经积累了数千次浏览。答案是——是的,有这样的运算符。而且,它在 JavaScript 中也有(从 ES2020 开始),并且在 TypeScript 中它“开箱即用”。它被称为 optional chaining(可选链),看起来就像大家熟悉的 ?.。在本文中,我们不仅会介绍如何使用它,还会深入底层:看看 TypeScript 编译器如何将这个优雅的语法转换为安全的 JavaScript 代码。
TypeScript 中的 ?. 运算符叫什么?
\n首先,我们从最重要的开始。在开发者社区中,这个运算符有不同的叫法:
\n- \n
- Optional chaining(可选链)—— ECMAScript 规范中的官方名称。 \n
- 安全访问运算符(Safe navigation operator)—— 来自其他语言,例如 C# 和 Swift。 \n
- Null 条件运算符(Null-conditional operator)—— 出现在微软的文档中。 \n
但本质是一样的:?. 允许你访问对象的属性,如果对象是 null 或 undefined,那么不会抛出错误,而是简单地返回 undefined。这不是魔法,而是一种语法糖,使代码更简洁、更安全。
基本语法
\n语法非常简单。与其这样写:
\nif (user && user.profile && user.profile.name) {\n console.log(user.profile.name);\n}\n你可以这样写:
\nconsole.log(user?.profile?.name);\n如果 user 是 undefined,那么整个链式调用会中断,并在控制台输出 undefined。没有错误,没有多余的检查。太棒了!
底层工作原理:TypeScript 编译为 JavaScript
\nTypeScript 是 JavaScript 的超集。它不直接执行代码,而是将其编译为普通的 JS。因此,让我们看看我们优雅的 user?.profile?.name 在编译后会变成什么。
示例:简单的 optional chaining
\nTypeScript 源代码:
\nconst name = user?.profile?.name;\n编译后(目标为 ES5):
\nvar name = user === null || user === void 0 ? void 0 : user.profile === null || user.profile === void 0 ? void 0 : user.profile.name;\n这里发生了什么?编译器生成了一个三元运算符,它检查每个嵌套层级是否为 null 或 undefined。如果值是 null 或 undefined,则返回 void 0(这是在旧引擎上可靠获取 undefined 的一种方式)。如果不是,则继续。本质上,这就是我们以前手动编写的代码,但现在由机器生成。
示例:带函数调用的 optional chaining
\n?. 运算符不仅适用于属性,也适用于函数调用。假设我们有一个可能不存在的 getAddress 方法:
const address = user?.getAddress?.();\n编译后:
\nvar address = user === null || user === void 0 ? void 0 : (typeof user.getAddress === "function" ? user.getAddress() : void 0);\n请注意:编译器不仅检查 null,还会确认 getAddress 是一个函数,然后才调用它。如果它不是函数,则返回 undefined。