这是本文档旧的修订版!
第三章:TypeScript 中的二元运算符与三元运算符
3.1 运算符的基本概念
在 TypeScript 中,运算符是用于对一个或多个值进行操作的符号。例如:
const result = 1 + 2;
在这段代码中:
1是一个操作数2是另一个操作数+是运算符1 + 2是一个表达式- 表达式最终会产生一个值,即
3
根据操作数数量的不同,运算符可以分为:
- 一元运算符:只需要一个操作数,例如
!flag、typeof value、-num - 二元运算符:需要两个操作数,例如
a + b、x === y - 三元运算符:需要三个操作数,例如
condition ? a : b
本章重点讲解二元运算符和三元运算符。
3.2 什么是二元运算符
二元运算符,英文通常称为 Binary Operator,是指需要两个操作数才能完成运算的运算符。
基本格式如下:
左操作数 运算符 右操作数
例如:
const sum = 10 + 20;
其中:
10是左操作数20是右操作数+是二元运算符
再比如:
const isAdult = age >= 18;
其中:
age是左操作数18是右操作数>=是二元运算符- 整个表达式的结果是一个布尔值
TypeScript 中的大多数常用运算符都是二元运算符。例如:
a + b a - b a * b a / b a % b a ** b a === b a !== b a > b a < b a >= b a <= b a && b a || b a ?? b a = b a += b
这些写法都有一个共同点:运算符左右两边各有一个表达式。
3.3 二元运算符的基本特点
二元运算符有以下几个重要特点。
第一,二元运算符必须有两个操作数。
例如下面的代码是正确的:
const result = a + b;
但是下面的代码是不完整的:
const result = a +;
因为 + 作为二元运算符时,右侧缺少一个操作数。
第二,二元运算符通常会产生一个新值。
例如:
const result = 3 * 4;
表达式 3 * 4 的结果是 12。
第三,不同的二元运算符有不同的返回类型。
例如:
const a = 1 + 2; // number const b = "a" + "b"; // string const c = 10 > 5; // boolean const d = true && false; // boolean
在 TypeScript 中,编译器会根据运算符和操作数类型推断表达式的结果类型。
第四,二元运算符可能涉及类型转换。
例如:
const result = "1" + 2;
这里的结果不是数字 3,而是字符串 “12”。因为当 + 的任意一侧是字符串时,JavaScript 会倾向于执行字符串拼接。
TypeScript 虽然增加了静态类型检查,但运行时行为仍然遵循 JavaScript。
3.4 算术二元运算符
算术运算符用于进行数学计算。常见的算术二元运算符包括:
| 运算符 | 名称 | 示例 |
|---|---|---|
+ | 加法 | a + b |
- | 减法 | a - b |
* | 乘法 | a * b |
/ | 除法 | a / b |
% | 取余 | a % b |
|
示例:
const a = 10; const b = 3; const sum = a + b; // 13 const diff = a - b; // 7 const product = a * b; // 30 const quotient = a / b; // 3.3333333333333335 const remainder = a % b; // 1 const power = a ** b; // 1000
在 TypeScript 中,这些算术运算符通常要求操作数是数字类型,或者是可以参与数值运算的类型。
例如:
const x: number = 10; const y: number = 5; const result = x - y;
这是合理的。
但是下面的代码通常不推荐:
const result = "10" - 5;
虽然 JavaScript 运行时可能会把字符串 “10” 转成数字 10,然后得到结果 5,但是在 TypeScript 中应尽量避免依赖隐式类型转换。
更推荐的写法是:
const value = Number("10");
const result = value - 5;
这样代码意图更加清晰。
3.5 加号运算符的特殊性
在 TypeScript 和 JavaScript 中,+ 是一个比较特殊的二元运算符。它既可以表示数字加法,也可以表示字符串拼接。
数字加法:
const result = 1 + 2; // 3
字符串拼接:
const message = "Hello, " + "TypeScript"; // "Hello, TypeScript"
数字与字符串相加:
const result = "Age: " + 18; // "Age: 18"
在这种情况下,数字会被转换为字符串,然后进行拼接。
例如:
const a = 1 + 2 + "3"; console.log(a); // "33"
分析过程如下:
1 + 2先执行,得到数字33 + “3”再执行,因为右侧是字符串,所以执行字符串拼接- 最终结果是字符串
“33”
再看另一个例子:
const b = "1" + 2 + 3; console.log(b); // "123"
分析过程如下:
“1” + 2先执行,得到字符串“12”“12” + 3再执行,得到字符串“123”
因此,在实际开发中,如果希望明确进行数字计算,应先把数据转换为数字:
const input = "100"; const result = Number(input) + 20;
如果希望明确进行字符串拼接,可以使用模板字符串:
const name = "Alice";
const age = 18;
const message = `${name} is ${age} years old.`;
模板字符串通常比多个 + 拼接更清晰。
3.6 赋值二元运算符
赋值运算符用于把右侧的值赋给左侧变量或属性。
最基本的赋值运算符是:
=
示例:
let count = 0; count = 10;
这里的 count = 10 是一个赋值表达式,其中:
- 左操作数是
count - 右操作数是
10 - 运算符是
=
常见的复合赋值运算符包括:
| 运算符 | 含义 | 等价写法 |
|---|---|---|
+= | 加后赋值 | a = a + b |
-= | 减后赋值 | a = a - b |
*= | 乘后赋值 | a = a * b |
/= | 除后赋值 | a = a / b |
%= | 取余后赋值 | a = a % b |
= | ||
&&= | 逻辑与赋值 | a = a && b 的近似形式 |
||= | 逻辑或赋值 | a = a || b 的近似形式 |
??= | 空值合并赋值 | a = a ?? b 的近似形式 |
示例:
let count = 10; count += 5; // 15 count -= 3; // 12 count *= 2; // 24 count /= 4; // 6 count %= 4; // 2
需要注意的是,复合赋值并不总是简单的文本替换。例如:
obj.value += 1;
大致可以理解为:
obj.value = obj.value + 1;
但在涉及 getter、setter 或复杂属性访问时,实际求值过程可能有更细致的规则。因此,在学习阶段可以先按等价写法理解,在深入阶段再研究求值顺序。
3.7 比较二元运算符
比较运算符用于比较两个值,并返回布尔值。
常见比较运算符如下:
| 运算符 | 名称 | 示例 |
|---|---|---|
== | 相等 | a == b |
!= | 不相等 | a != b |
=== | 严格相等 | a === b |
!== | 严格不相等 | a !== b |
> | 大于 | a > b |
< | 小于 | a < b |
>= | 大于等于 | a >= b |
⇐ | 小于等于 | a ⇐ b |
示例:
const age = 20; const a = age > 18; // true const b = age < 18; // false const c = age >= 20; // true const d = age <= 20; // true
在 TypeScript 中,推荐优先使用严格相等 === 和严格不相等 !==,而不是 == 和 !=。
原因是 == 会进行隐式类型转换。
例如:
console.log(1 == "1"); // true console.log(1 === "1"); // false
第一行中,== 会尝试进行类型转换,所以数字 1 和字符串 “1” 被认为相等。
第二行中,=== 不会进行这种隐式类型转换。数字 1 和字符串 “1” 类型不同,因此结果是 false。
在 TypeScript 项目中,使用 === 可以减少很多潜在错误。
3.8 逻辑二元运算符
逻辑二元运算符主要包括:
| 运算符 | 名称 | 示例 |
|---|---|---|
&& | 逻辑与 | a && b |
|| | 逻辑或 | a || b |
虽然逻辑非 ! 也属于逻辑运算符,但它是一元运算符,不属于本节重点。
逻辑与 && 的基本规则是:
- 如果左侧是假值,则返回左侧
- 如果左侧是真值,则返回右侧
示例:
const result1 = true && "hello"; // "hello" const result2 = false && "hello"; // false
逻辑或 || 的基本规则是:
- 如果左侧是真值,则返回左侧
- 如果左侧是假值,则返回右侧
示例:
const result1 = "hello" || "default"; // "hello" const result2 = "" || "default"; // "default"
需要注意的是,&& 和 || 返回的不一定是布尔值,它们返回的是其中一个操作数的值。
例如:
const name = ""; const displayName = name || "匿名用户"; console.log(displayName); // "匿名用户"
这里 name 是空字符串,属于假值,因此 name || “匿名用户” 返回右侧的 “匿名用户”。
JavaScript 中常见的假值包括:
false0-00n*null*undefined*NaN除了这些值之外,大多数值都是真值。 —- ===== 3.9 逻辑短路特性 ===== 逻辑运算符具有短路特性。 所谓短路,是指如果通过左操作数已经能够确定整个表达式的结果,那么右操作数就不会再被执行。 逻辑与&&的短路: <code typescript> function sayHello() { console.log(“hello”); return true; } const result = false && sayHello(); </code> 在这段代码中,sayHello()不会被调用。因为false && 任意值的结果都可以由左侧的false决定。 逻辑或||的短路: <code typescript> function getDefaultName() { console.log(“get default name”); return “匿名用户”; } const name = “Alice”; const displayName = name || getDefaultName(); </code> 这里getDefaultName()不会被调用,因为左侧name是真值,整个表达式直接返回name。 短路特性在实际开发中非常常见。例如: <code typescript> isReady && start(); </code> 这表示:如果isReady为真,就执行start()。 但是在 TypeScript 中,为了代码可读性,很多团队更推荐写成: <code typescript> if (isReady) { start(); } </code> 尤其是在有副作用的函数调用中,显式的if语句通常更容易理解。 —- ===== 3.10 空值合并二元运算符 ===== 空值合并运算符是: <code typescript> ?? </code> 它也是一个二元运算符,因为它需要左操作数和右操作数。 基本格式如下: <code typescript> const result = left ?? right; </code> 规则是: * 如果左侧是null或undefined,则返回右侧 * 否则返回左侧 示例: <code typescript> const username = null; const displayName = username ?? “匿名用户”; console.log(displayName); “匿名用户” </code> 如果左侧不是“匿名用户”null或undefined: <code typescript> const username = “”; const displayName = username ?? “匿名用户”; console.log(displayName); “” </code> 这里结果是空字符串,而不是。因为空字符串虽然是假值,但不是null或undefined。 这正是??和||的重要区别。 —- ===== 3.11 ?? 与 || 的区别 =====??只关心左侧是否是null或undefined。||关心左侧是否是假值。 对比示例: <code typescript> const a = 0 ?? 100; const b = 0 || 100; console.log(a); 0 console.log(b); 100 </code> 分析: *0 ?? 100中,左侧是0,不是null或undefined,所以返回0*0 || 100中,左侧是0,是假值,所以返回100再看空字符串: <code typescript> const title1 = “” ?? “默认标题”; const title2 = “” || “默认标题”; console.log(title1); “” console.log(title2); “默认标题” </code> 因此,当你希望只有在值“缺失”时才使用默认值,应优先使用??。 例如: <code typescript> interface Config { retryCount?: number; } const config: Config = { retryCount: 0 }; const retryCount = config.retryCount ?? 3; console.log(retryCount); 0 </code> 这里用户明确设置了retryCount: 0,表示不重试。如果使用||: <code typescript> const retryCount = config.retryCount || 3; </code> 结果会变成3,这可能不是我们想要的。 —- ===== 3.12 空值合并赋值运算符 ??= ===== 除了??,TypeScript 中还有??=。??=是空值合并赋值运算符。 它的含义是:如果左侧变量当前是null或undefined,就把右侧的值赋给它;否则保持原值不变。 示例: <code typescript> let name: string | null = null; name ??= “匿名用户”; console.log(name); “匿名用户” </code> 如果变量已有值: <code typescript> let name = “Alice”; name ??= “匿名用户”; console.log(name); “Alice” </code> 可以把: <code typescript> value ??= defaultValue; </code> 理解为: <code typescript> value = value ?? defaultValue; </code> 但和其他复合赋值运算符一样,在复杂属性访问场景下,实际求值过程并不完全等同于简单文本替换。 —- ===== 3.13 位运算二元运算符 ===== 位运算符用于按二进制位进行操作。常见的位运算二元运算符包括: ^ 运算符 ^ 名称 ^ 示例 ^ |&| 按位与 |a & b| ||| 按位或 |a | b| |^| 按位异或 |a ^ b| |«| 左移 |a « b| |»| 有符号右移 |a » b| |»>| 无符号右移 |a »> b| 注意:按位非~是一元运算符,不属于二元运算符。 示例: <code typescript> const a = 5; 二进制 0101 const b = 3; 二进制 0011 console.log(a & b); 1,二进制 0001 console.log(a | b); 7,二进制 0111 console.log(a ^ b); 6,二进制 0110 </code> 位运算在普通业务代码中不算特别常见,但在以下场景中可能会用到: * 权限标记 * 状态压缩 * 图形处理 * 加密算法 * 底层性能优化 * 网络协议处理 例如,用位标记表示权限: <code typescript> const READ = 1; 0001 const WRITE = 2; 0010 const DELETE = 4; 0100 let permission = READ | WRITE; const canRead = (permission & READ) !== 0; const canDelete = (permission & DELETE) !== 0; console.log(canRead); true console.log(canDelete); false </code> 这里: *READ | WRITE得到组合权限 *permission & READ用于检查是否包含读取权限 —- ===== 3.14 关系二元运算符 ===== 关系运算符用于判断两个值之间的某种关系。除了前面讲到的大小比较之外,TypeScript 中还常见以下关系运算符: ^ 运算符 ^ 名称 ^ 示例 ^ |in| 属性存在判断 |key in object| |instanceof| 实例判断 |obj instanceof ClassName| ==== 3.14.1 in 运算符 ====in用于判断某个属性名是否存在于对象中,包括对象自身属性和原型链上的属性。 示例: <code typescript> const user = { name: “Alice”, age: 18 }; console.log(“name” in user); true console.log(“email” in user); false </code> 在 TypeScript 中,in还经常用于类型收窄。 例如: <code typescript> type Dog = { bark: () ⇒ void; }; type Cat = { meow: () ⇒ void; }; function makeSound(animal: Dog | Cat) { if (“bark” in animal) { animal.bark(); } else { animal.meow(); } } </code> 在if (“bark” in animal)分支中,TypeScript 可以推断animal更可能是Dog类型,因此允许调用animal.bark()。 ==== 3.14.2 instanceof 运算符 ====instanceof用于判断一个对象是否是某个构造函数或类的实例。 示例: <code typescript> class User { constructor(public name: string) {} } const user = new User(“Alice”); console.log(user instanceof User); true console.log(user instanceof Date); false </code>instanceof也可以用于类型收窄: <code typescript> function formatValue(value: string | Date) { if (value instanceof Date) { return value.toISOString(); } return value.toUpperCase(); } </code> 在value instanceof Date分支中,TypeScript 会把value视为Date类型;在另一个分支中,value会被视为string类型。 —- ===== 3.15 类型相关的二元运算注意事项 ===== TypeScript 的核心优势在于静态类型检查。二元运算符在 TypeScript 中不仅要考虑 JavaScript 的运行时行为,还要考虑编译阶段的类型规则。 例如: <code typescript> const a: number = 10; const b: string = “20”; const result = a - b; </code> 这段代码在 JavaScript 中运行时可能得到-10,因为字符串“20”会被转换为数字。但在 TypeScript 中,这通常会被认为是不合适的写法,因为-运算符两侧应是可进行数值运算的值。 推荐写法: <code typescript> const a: number = 10; const b: string = “20”; const result = a - Number(b); </code> 又如: <code typescript> const value: string | undefined = undefined; const result = value.toUpperCase(); </code> 如果开启了strictNullChecks,TypeScript 会提示value可能是undefined。 可以使用??提供默认值: <code typescript> const value: string | undefined = undefined; const result = (value ?? “”).toUpperCase(); </code> 也可以使用条件判断: <code typescript> if (value !== undefined) { console.log(value.toUpperCase()); } </code> —- ===== 3.16 三元运算符的基本概念 ===== 三元运算符,英文通常称为 Ternary Operator,是指需要三个操作数的运算符。 在 TypeScript 和 JavaScript 中,最常见也几乎是唯一常用的三元运算符是条件运算符: <code typescript> condition ? expressionIfTrue : expressionIfFalse </code> 它也常被称为“条件三元运算符”。 结构如下: <code typescript> 条件表达式 ? 条件为真时的结果 : 条件为假时的结果 </code> 例如: <code typescript> const age = 20; const message = age >= 18 ? “成年人” : “未成年人”; </code> 这里: *age >= 18是第一个操作数,也是条件表达式 *“成年人”是第二个操作数,表示条件为真时的结果 *“未成年人”是第三个操作数,表示条件为假时的结果 *?:共同构成三元运算符的语法结构 —- ===== 3.17 三元运算符与 if else 的关系 ===== 三元运算符可以看作是if else的表达式形式。 例如: <code typescript> const age = 20; let message: string; if (age >= 18) { message = “成年人”; } else { message = “未成年人”; } </code> 可以改写为: <code typescript> const age = 20; const message = age >= 18 ? “成年人” : “未成年人”; </code> 两者的区别是: *if else是语句,更适合执行多行逻辑 * 三元运算符是表达式,更适合根据条件返回一个值 例如,如果只是根据条件决定一个变量的值,三元运算符会比较简洁: <code typescript> const buttonText = isLoading ? “加载中…” : “提交”; </code> 如果条件分支中有多步操作,则推荐使用if else: <code typescript> if (isLoading) { console.log(“开始加载”); showSpinner(); disableButton(); } else { console.log(“加载结束”); hideSpinner(); enableButton(); } </code> 不要为了追求简短而滥用三元运算符。 —- ===== 3.18 三元运算符的返回类型推断 ===== TypeScript 会根据三元运算符两个结果分支推断最终类型。 例如: <code typescript> const result = true ? 1 : 2; </code> 这里result的类型会被推断为数字相关类型。 如果两个分支是不同类型: <code typescript> const result = Math.random() > 0.5 ? “success” : 0; </code> 那么result的类型会被推断为: <code typescript> string | number </code> 也就是联合类型。 再看一个例子: <code typescript> const status = isSuccess ? “success” : “error”; </code> 如果isSuccess是布尔值,TypeScript 可能会把status推断为字符串字面量联合类型: <code typescript> “success” | “error” </code> 这在实际开发中非常有用。 例如: <code typescript> type Status = “success” | “error” | “loading”; const status: Status = isLoading ? “loading” : isSuccess ? “success” : “error”; </code> 不过上面这个例子使用了嵌套三元运算符,虽然可以工作,但可读性需要谨慎考虑。 —- ===== 3.19 嵌套三元运算符 ===== 三元运算符可以嵌套使用,但不推荐过度嵌套。 例如: <code typescript> const score = 85; const level = score >= 90 ? “优秀” : score >= 60 ? “及格” : “不及格”; </code> 这段代码的含义是: * 如果score >= 90,结果是“优秀”* 否则继续判断score >= 60* 如果score >= 60,结果是“及格”* 否则结果是“不及格”虽然这段代码不长,但如果条件继续增加,就会变得难以阅读。 例如: <code typescript> const label = type === “admin” ? “管理员” : type === “editor” ? “编辑者” : type === “guest” ? “访客” : type === “anonymous” ? “匿名用户” : “未知用户”; </code> 这种写法虽然合法,但可读性较差。 更推荐使用对象映射或switch: <code typescript> const labels: Record<string, string> = { admin: “管理员”, editor: “编辑者”, guest: “访客”, anonymous: “匿名用户” }; const label = labels[type] ?? “未知用户”; </code> 或者: <code typescript> let label: string; switch (type) { case “admin”: label = “管理员”; break; case “editor”: label = “编辑者”; break; case “guest”: label = “访客”; break; case “anonymous”: label = “匿名用户”; break; default: label = “未知用户”; } </code> 原则是:三元运算符适合简单条件,复杂分支应使用更清晰的结构。 —- ===== 3.20 三元运算符在 JSX 和模板中的使用 ===== 在 React 或其他模板场景中,三元运算符非常常见。 例如 React JSX 中: <code tsx> function Button({ isLoading }: { isLoading: boolean }) { return ( <button> {isLoading ? “加载中…” : “提交”} </button> ); } </code> 再例如根据用户状态显示不同内容: <code tsx> function UserPanel({ isLoggedIn }: { isLoggedIn: boolean }) { return ({isLoggedIn ? <p>欢迎回来</p> : <p>请先登录</p>} </div> );} </code>
在 JSX 中,三元运算符比
if else更常用于“根据条件渲染不同内容”的场景。但是,如果条件逻辑复杂,建议提前在函数体中计算好结果:
function StatusText({ status }: { status: "loading" | "success" | "error" }) { let text: string; if (status === "loading") { text = "加载中"; } else if (status === "success") { text = "加载成功"; } else { text = "加载失败"; } return <p>{text}</p>; }这样 JSX 部分会更清晰。
3.21 三元运算符与空值合并运算符的组合
三元运算符经常和
??、?.等运算符一起使用。例如:
const displayName = user.name ?? "匿名用户";
如果逻辑更复杂:
const displayName = user.name ? user.name : user.nickname ?? "匿名用户";
这里的逻辑是:
- 如果
user.name是真值,使用user.name - 否则尝试使用
user.nickname - 如果
user.nickname是null或undefined,使用“匿名用户”
但要注意,
user.name ? user.name : …会把空字符串当作假值。如果空字符串也应该被认为是有效值,就应该使用??:const displayName = user.name ?? user.nickname ?? "匿名用户";
这表示只有当前一个值为
null或undefined时,才继续使用下一个值。
3.22 运算符优先级简介
当一个表达式中出现多个运算符时,运算符优先级决定了先执行哪个。
例如:
const result = 1 + 2 * 3;
结果是
7,而不是9。因为乘法*的优先级高于加法+。如果希望先执行加法,可以使用括号:
const result = (1 + 2) * 3;
这时结果是
9。在实际开发中,不建议过度依赖记忆运算符优先级。对于稍复杂的表达式,推荐使用括号明确表达意图。
例如:
const result = a && b || c;
可以改写为:
const result = (a && b) || c;
或者:
const result = a && (b || c);
根据实际需求明确分组。
3.23 ?? 与 &&、|| 混用时的注意事项
空值合并运算符
??和逻辑与&&、逻辑或||混用时需要特别注意。在 JavaScript 和 TypeScript 中,不能在没有括号的情况下直接混用
??和||或&&。例如下面的写法是错误的:
const value = a ?? b || c;
应该使用括号明确优先级:
const value = (a ?? b) || c;
或者:
const value = a ?? (b || c);
这两种写法含义不同。
第一种:
const value = (a ?? b) || c;
表示先判断
a是否为空值。如果a不是null或undefined,先得到a;否则得到b。然后再把这个结果与c做逻辑或。第二种:
const value = a ?? (b || c);
表示如果
a不是null或undefined,直接返回a;否则再计算b || c。示例:
const a = 0; const b = "B"; const c = "C"; const value1 = (a ?? b) || c; const value2 = a ?? (b || c); console.log(value1); // "C" console.log(value2); // 0
分析:
value1中,a ?? b的结果是0,然后0 || c的结果是“C”value2中,a不是空值,所以a ?? (b || c)直接返回0
因此混用这些运算符时,必须认真使用括号。
3.24 二元运算符和三元运算符的可读性原则
运算符虽然可以让代码更简洁,但并不意味着越短越好。
推荐原则如下:
- 简单表达式可以使用运算符
- 复杂逻辑不要强行压缩到一行
- 涉及多个运算符时优先使用括号
- 三元运算符适合“二选一”赋值
- 嵌套三元运算符要谨慎使用
- 对默认值处理优先考虑
?? - 对布尔条件组合要注意短路行为
- 对数字和字符串混合运算要避免隐式转换
例如,不推荐:
const result = a ? b ? c : d : e ? f : g;
更推荐:
let result: string; if (a) { result = b ? c : d; } else { result = e ? f : g; }虽然代码行数更多,但逻辑更清晰。
3.25 实际开发示例:默认配置
假设有一个配置对象:
interface Options { timeout?: number; retry?: number; debug?: boolean; } function createOptions(options: Options) { const timeout = options.timeout ?? 3000; const retry = options.retry ?? 3; const debug = options.debug ?? false; return { timeout, retry, debug }; }这里使用
??而不是||是非常重要的。因为以下配置是有意义的:
createOptions({ timeout: 0, retry: 0, debug: false });如果使用
||:const timeout = options.timeout || 3000; const retry = options.retry || 3; const debug = options.debug || false;
那么
timeout: 0和retry: 0会被错误地替换为默认值。使用
??可以避免这个问题。
3.26 实际开发示例:状态显示
假设一个请求状态可能是:
type RequestStatus = "idle" | "loading" | "success" | "error";
可以用三元运算符显示文字:
function getStatusText(status: RequestStatus): string { return status === "loading" ? "加载中" : status === "success" ? "加载成功" : status === "error" ? "加载失败" : "等待开始"; }虽然这段代码可以正常工作,但嵌套三元较多。
更推荐:
function getStatusText(status: RequestStatus): string { const map: Record<RequestStatus, string> = { idle: "等待开始", loading: "加载中", success: "加载成功", error: "加载失败" }; return map[status]; }如果状态值可能来自外部字符串:
function getStatusText(status: string): string { const map: Record<string, string> = { idle: "等待开始", loading: "加载中", success: "加载成功", error: "加载失败" }; return map[status] ?? "未知状态"; }这里
??用来处理找不到映射结果的情况。
3.27 实际开发示例:表单校验
二元运算符和三元运算符在表单校验中非常常见。
例如:
function validateUsername(username: string): string { return username.length >= 3 ? "" : "用户名至少需要 3 个字符"; }如果有多个条件:
function validatePassword(password: string): string { if (password.length < 8) { return "密码至少需要 8 个字符"; } if (!/[A-Z]/.test(password)) { return "密码至少需要包含一个大写字母"; } if (!/[0-9]/.test(password)) { return "密码至少需要包含一个数字"; } return ""; }这里不建议写成复杂嵌套三元:
function validatePassword(password: string): string { return password.length < 8 ? "密码至少需要 8 个字符" : !/[A-Z]/.test(password) ? "密码至少需要包含一个大写字母" : !/[0-9]/.test(password) ? "密码至少需要包含一个数字" : ""; }虽然它是合法的,但可读性不如多个
if。
3.28 实际开发示例:权限判断
权限判断中常用逻辑二元运算符。
例如:
interface User { role: "admin" | "editor" | "guest"; active: boolean; } function canEdit(user: User): boolean { return user.active && (user.role === "admin" || user.role === "editor"); }这里:
user.active && …表示用户必须处于启用状态user.role === “admin” || user.role === “editor”表示角色必须是管理员或编辑者- 括号用于明确逻辑组合关系
如果不用括号:
return user.active && user.role === "admin" || user.role === "editor";
虽然按照优先级也能执行,但可读性不如带括号的版本,而且容易让读者误解。
更清晰的写法:
function canEdit(user: User): boolean { const isActive = user.active; const hasRole = user.role === "admin" || user.role === "editor"; return isActive && hasRole; }这种写法在复杂业务逻辑中更容易维护。
3.29 常见错误一:把 || 当作默认值处理的一切方案
很多初学者会这样写:
const pageSize = inputPageSize || 20;
这在某些场景下没有问题,但如果
inputPageSize可能是0,就会出现问题。例如:
const inputPageSize = 0; const pageSize = inputPageSize || 20; console.log(pageSize); // 20
如果
0是一个合法值,那么这里就错了。更推荐:
const pageSize = inputPageSize ?? 20;
这样只有当
inputPageSize是null或undefined时才使用默认值。
3.30 常见错误二:三元运算符嵌套过深
例如:
const result = a ? b ? c : d : e ? f : g;这类代码虽然简短,但阅读成本较高。
建议改写为
if else:let result; if (a) { if (b) { result = c; } else { result = d; } } else { if (e) { result = f; } else { result = g; } }或者根据业务场景抽成函数:
function getResult() { if (a && b) { return c; } if (a && !b) { return d; } if (!a && e) { return f; } return g; } const result = getResult();函数化往往能让复杂条件更容易测试和维护。
3.31 常见错误三:忽略 TypeScript 的联合类型
三元运算符可能产生联合类型。
例如:
const value = condition ? "ok" : 0;
此时
value的类型可能是:string | number
如果后续直接调用字符串方法:
console.log(value.toUpperCase());
TypeScript 会报错,因为
number类型没有toUpperCase方法。正确做法是先进行类型判断:
if (typeof value === "string") { console.log(value.toUpperCase()); }或者保证三元表达式两个分支返回同一种类型:
const value = condition ? "ok" : "0"; console.log(value.toUpperCase());
3.32 常见错误四:误解 && 和 || 的返回值
很多人以为
&&和||一定返回布尔值,其实它们返回的是操作数本身。例如:
const result = "hello" && 123; console.log(result); // 123
再比如:
const result = "" || "default"; console.log(result); // "default"
如果你需要明确得到布尔值,可以使用
Boolean()或双重逻辑非!!:const value = "hello"; const bool1 = Boolean(value); const bool2 = !!value;
在 TypeScript 中,清楚区分“返回某个值”和“返回布尔值”非常重要。
3.33 常见错误五:对象比较使用 === 判断内容相等
比较运算符
===比较对象时,比较的是引用地址,而不是对象内容。例如:
const a = { name: "Alice" }; const b = { name: "Alice" }; console.log(a === b); // false虽然两个对象内容看起来一样,但它们是两个不同对象。
如果是同一个引用:
const a = { name: "Alice" }; const b = a; console.log(a === b); // true数组也是一样:
console.log([1, 2] === [1, 2]); // false
如果需要比较内容,需要自己逐项比较,或者使用专门的工具函数。
简单数组比较示例:
function isSameNumberArray(a: number[], b: number[]): boolean { if (a.length !== b.length) { return false; } return a.every((item, index) => item === b[index]); }
3.34 本章小结
本章详细介绍了 TypeScript 中的二元运算符与三元运算符。
二元运算符是需要两个操作数的运算符,例如:
a + b a === b a && b a ?? b
常见二元运算符包括:
- 算术运算符
- 赋值运算符
- 比较运算符
- 逻辑运算符
- 空值合并运算符
- 位运算符
- 关系运算符
三元运算符是需要三个操作数的运算符。TypeScript 中最常见的三元运算符是条件运算符:
condition ? valueIfTrue : valueIfFalse
它适合根据条件选择一个值,但不适合承载过于复杂的业务逻辑。
在实际开发中,应特别注意以下几点:
??是空值合并运算符,也是二元运算符??只在左侧为null或undefined时使用右侧||会在左侧是假值时使用右侧&&和||不一定返回布尔值- 三元运算符适合简单二选一
- 嵌套三元运算符要谨慎使用
- 比较对象时,
===比较的是引用,而不是内容 - 复杂表达式建议使用括号提高可读性
- TypeScript 会根据运算符推断表达式类型
- 当三元运算符两个分支类型不同,结果通常是联合类型
理解这些规则之后,就能更准确地阅读和编写 TypeScript 表达式,也能避免许多由隐式类型转换、默认值处理、逻辑短路和类型推断造成的常见错误。
- typescript/第三章下_二元运算符与三元运算符.1777292790.txt.gz
- 最后更改: 2026/04/27 20:26
- 由
张叶安