- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
本文以 The Concise TypeScript Book(当前开源仓库typescript-book)印尼语版第 13 章"Properti Opsional"(可选属性)为骨架,完整继承原文档的两个核心知识点——用?声明可选属性、为可选属性指定默认值——并结合仓库中strictnullchecks、type-manipulation、narrowing、object-types等章节的真实内容,深入讲解可选属性在严格空值检查下的真实类型语义、与Partial/Required工具类型的配合方式,以及在实际项目中处理"可能缺失"数据时的完整方案。
用?声明可选属性
原文档(optional-properties.md)开篇给出的定义是:对象可以通过在属性名称末尾添加问号?来指定可选属性(Optional Properties)。这是 TypeScript 类型系统中控制"某属性是否必须存在"的基础机制:
type X = { a: number; b?: number; // Optional };这段代码定义了对象类型X:
a: number是必填属性——任何X类型的值都必须提供a,否则类型检查不通过;b?: number是可选属性——b可以省略,也可以在提供时取任意number值。
也就是说,以下两个对象都合法:
const x1: X = { a: 1 }; // 省略可选属性 b,合法 const x2: X = { a: 1, b: 2 }; // 提供可选属性 b,合法而同仓库中对象类型章节在介绍 interface 时给出的email?: string写法,与上面的 type alias 写法语义一致:
interface User { name: string; age: number; email?: string; }可选属性在真实项目中最常见的场景就是描述"字段可能缺失"的结构,例如 API 响应、用户配置、表单数据等。
严格空值检查下,可选属性的类型是什么
单独理解?还不够,可选属性的实际行为与编译器选项strictNullChecks强相关。同仓库的 strictnullchecks 章节对这一选项的定义是:
strictNullChecks是 TypeScript 的编译器选项,用于强制执行严格的空值检查。当启用此选项时,变量和参数只有在被显式声明为null | undefined联合类型时,才能被赋值为null或undefined。如果变量或参数未被显式声明为可空,TypeScript 将产生错误,以防止运行时的潜在错误。
把这条规则映射到可选属性上:
- 未开启
strictNullChecks时,undefined和null是任何类型的子类型,因此读取x.b几乎不会出现类型层面的报错,空值问题被"静默"掩盖; - 开启
strictNullChecks时,可选属性b?: number在语义上等价于"该属性可能缺失"。此时从对象上读取b,其类型会被视为number | undefined,任何把b直接当number使用的地方(比如直接参与算术运算)都会报错,逼迫开发者先处理"缺失"分支。
这正是原文档第二个例子必须引入"默认值"的原因——只有处理了undefined的可能性,可选属性才能安全参与计算。
为可选属性指定默认值(解构默认值)
原文档给出的第二个核心示例是:当属性是可选的时,可以指定默认值。其完整代码如下:
type X = { a: number; b?: number; }; const x = ({ a, b = 100 }: X) => a + b;这里的关键技术是参数解构中的默认值(destructuring default value),其语义是:
- 当传入对象的
b不存在(即调用方省略了该可选属性)时,b取默认值100; - 当
b被显式传入且值为undefined时,同样回退到100; - 当
b被传入具体数字时,使用传入值。
对应的调用示例如下:
console.log(x({ a: 1 })); // 101 (b 缺失,回退到 100) console.log(x({ a: 1, b: 2 })); // 3 (使用传入的 b)这样一来,函数体内部的a + b中b的类型收窄为确定的number,在strictNullChecks开启时也能通过类型检查。这种"可选属性 + 解构默认值"的组合,本质上是把b = b ?? 100这类手动空值兜底前置到了参数声明处,代码更紧凑、契约更清晰。
需要注意默认值的触发条件是"属性缺失或值为undefined",而不是 falsy:如果调用方显式传入b: 0,函数会使用0而不是回退到100。这与 JavaScript 解构默认值的标准语义一致。
可选属性与类型注解的配合规范
可选属性最常出现在对象类型的声明中,而类型注解章节给出的通用建议对含可选属性的代码同样适用:考虑对签名(signature)进行类型注解,但不在函数体内局部变量上注解,并且始终给对象字面量加上类型。
结合可选属性,一个典型的"签名带注解 + 默认值兜底"的完整写法是:
type Config = { host: string; port?: number; // 可选属性 timeout?: number; // 可选属性 }; const startServer = ({ host, port = 8080, timeout = 3000 }: Config): string => { return `listening on ${host}:${port} (timeout ${timeout}ms)`; };这里所有可选属性都通过解构默认值获得了确定类型,返回值注解string则符合该章节"对复杂函数写明返回类型"的建议。
用工具类型批量控制可选性:Partial<T> 与 Required<T>
原文档的?是逐属性手工标记,而在处理"已有类型的整体可选化/必选化"时,类型操作章节中的两个内置工具类型更为高效:
Partial<T>——构造一个所有属性 T 都被设为可选的类型:
type Person = { name: string; age: number; }; type A = Partial<Person>; // { name?: string | undefined; age?: number | undefined; }Required<T>——构造一个所有属性 T 都被设为必选的类型(与Partial正好互逆):
type Person = { name?: string; age?: number; }; type A = Required<Person>; // { name: string; age: number; }这两个工具类型正是"可选属性"概念的类型级批量操作:Partial<Person>等价于把Person的每个属性名后自动加上?,Required<T>则反向去除可选标记。它们在实际开发中最常用的两个场景:
- 更新接口/表单场景:数据库更新、表单局部提交通常只携带部分字段,参数类型写作
Partial<User>即可精确表达"所有字段均可选"; - 从宽松类型收紧:当某个中间数据结构大量使用可选属性时,在边界处用
Required<...>强制补全,避免undefined向下游扩散。
从该章节给出的结果注释(如{ name?: string | undefined; ... })还可以印证前文所述:开启严格空值检查时,可选属性展开后的类型确实显式携带了| undefined。
可选属性的读写语义与 readonly 的对比
理解可选属性时容易与只读属性混淆,这里对照仓库中的相邻章节说明二者正交:
- 可选(
?)控制的是属性是否存在,见原文档 optional-properties.md; - 只读(
readonly)控制的是属性能否被重新赋值,readonly-properties 章节的说明是:readonly修饰符确保属性"不能被重新赋值,但不提供完全的不可变性保证",且Readonly<T>工具类型可将整个类型的属性批量设为只读。
两者可以自由组合,例如"可能存在且存在后不可修改"的字段:
type Connection = { id: string; endpoint?: string; // 可选 createdAt: readonly { // 必填,但对象本身声明了只读约束的字段 value: number; }; };读取可选属性前的类型收窄
即便声明了可选属性,直接读取它仍会带着undefined的可能。类型收窄(narrowing)章节列举的多种收窄手段对可选属性同样有效,其中最常用的是基于 truthiness 的收窄:
const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } };对应到可选属性,典型写法有两种:
const config: X = { a: 1 }; // 方式一:显式判空 if (config.b !== undefined) { console.log(config.b + 1); // b 已收窄为 number } // 方式二:nullish coalescing(??)兜底 const value = config.b ?? 0;配合这些手段,"可选属性 → 判空/默认值 → 收窄为确定类型"就形成了处理可能缺失数据的标准链路。
小结与延伸阅读
回到原文档的两个核心结论并加以纵深:
- 声明:在属性名后加
?即可声明可选属性(b?: number),该属性可整体缺失; - 默认值:在函数参数解构中使用
b = 100形式的默认值,可让缺失或为undefined的可选属性回退到确定值,从而安全参与后续计算; - 纵深:在
strictNullChecks开启时,可选属性的读取类型携带| undefined,需要通过判空、??或解构默认值收窄;批量场景则交给Partial<T>/Required<T>工具类型。
如需继续深入类型系统,建议按以下仓库内文档顺序阅读(均为印尼语版第 13 章周边章节):
- 类型注解(第 12 章)
- 只读属性(第 14 章)
- strictNullChecks(第 19 章)
- 类型收窄(第 21 章)
- 对象类型(第 28 章)
- 类型操作与工具类型(第 61 章)
- 完整目录
- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
相关推荐
TypeScript 可选属性实战指南:深入 The Concise TypeScript Book 的 `?` 语法与默认值
TypeScript 可选属性实战指南:深入 The Concise TypeScript Book 的 ? 语法与默认值 本篇指南基于 The Concise
文档教程The Concise TypeScript Book 第 12 章深度解读:TypeScript 类型注解(Type Annotations)完整实战指南
The Concise TypeScript Book 第 12 章深度解读:TypeScript 类型注解(Type Annotations)完整实战指南 类
文档教程The Concise TypeScript Book 精读:TypeScript readonly 只读属性完整实战指南
The Concise TypeScript Book 精读:TypeScript readonly 只读属性完整实战指南 本篇对应 The Concise T
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考