TypeScript及其永恒问题:如何使类型可为空?
\n\n你好,同行!如果你曾经用TypeScript写过代码,那么你肯定遇到过这样的情况:函数可能返回值,也可能不返回。或者变量不是立即初始化的。这时就出现了一个至关重要的问题:如何声明一个可能为null或undefined的类型?
这个问题是Stack Overflow上最热门的问题之一(有数万次浏览!),这并非偶然。TypeScript很严格,但也很公平。它不允许我们简单地将null赋值给一个类型为string的变量。这非常好,因为它能避免大量错误。但是,当nullable是业务逻辑的一部分时,该怎么办?让我们来探讨一下。
在本文中,我们不仅会给出一个干巴巴的答案。我们将从基础到高级技术,分析常见错误,并学习如何编写既安全又优雅的代码。开始吧!
\n\n基础:联合类型和Null
\n\n使类型可为空的最简单直接的方法是使用联合类型。如果一个变量可以是字符串或null,我们可以这样写:
\n\nlet userName: string | null = null;\nuserName = "Алиса"; // 可以\nuserName = null; // 也可以\n\n\n同样的方法也适用于undefined。在TypeScript中,null和undefined是不同的类型(除非启用了--strictNullChecks标志,稍后会提到)。
let age: number | undefined;\nage = 25;\nage = undefined; // 可以工作\n\n\n重要提示:如果你使用了| null或| undefined,TypeScript会强制你在使用值之前进行检查。尝试对可能为null的对象调用方法会导致编译错误。这正是我们喜爱TS的“安全性”。
现实生活中的例子:查找用户
\n\ninterface User {\n id: number;\n name: string;\n}\n\nfunction findUser(id: number): User | null {\n // ...查找逻辑\n if (id === 1) {\n return { id: 1, name: "Иван" };\n }\n return null;\n}\n\nconst user = findUser(2);\n// user.name; // 错误!'user' 可能为 null\nif (user) {\n console.log(user.name); // 现在安全了\n}\n\n\n看到了吗?编译器会提示你:“嘿,检查一下,否则会出错!”。这可以节省数小时的调试时间。
\n\n可选参数和属性(可选链)
\n\n通常,不仅变量可以是nullable,对象的字段或函数的参数也可以是nullable。为此,TypeScript有一种特殊的语法——可选修饰符(问号?)。
函数的可选参数
\n\nfunction greet(name?: string) {\n if (name) {\n return `Привет, ${name}!`;\n }\n return "Привет, незнакомец!";\n}\n\nconsole.log(greet("Петя")); // 你好,Петя!\nconsole.log(greet()); // 你好,陌生人!\n\n\n参数name的类型会自动变为string | undefined。请注意:如果参数是可选的,你可以不传递它,TS不会报错。
接口的可选属性
\n\ninterface Config {\n url: string;\n port?: number; // 可以是 number 或 undefined\n}\n\nconst config1: Config = { url: "http://localhost" }; // port 缺失\nconst config2: Config = { url: "http://localhost", port: 3000 };\n\n\n当访问config1.port时,TypeScript会告诉你它是number | undefined。这是正确的——我们不知道端口是否已设置。
可选链——你最好的朋友
\n\n随着ES2020和TypeScript 3.7的出现,我们有一种优雅的方式来安全地访问可能为null或undefined的嵌套属性。这被称为可选链