typescript:第三章下_二元运算符与三元运算符

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
typescript:第三章下_二元运算符与三元运算符 [2026/04/27 20:25] – 移除 - 外部编辑 (未知日期) 127.0.0.1typescript:第三章下_二元运算符与三元运算符 [2026/04/27 20:31] (当前版本) 张叶安
行 1: 行 1:
 +====== 第三章:TypeScript 中的二元运算符与三元运算符 ======
  
 +
 +
 +===== 3.1 运算符的基本概念 =====
 +
 +在 TypeScript 中,运算符是用于对一个或多个值进行操作的符号。例如:
 +
 +<code typescript>
 +const result = 1 + 2;
 +</code>
 +
 +在这段代码中:
 +
 +  * ''1'' 是一个操作数
 +  * ''2'' 是另一个操作数
 +  * ''+'' 是运算符
 +  * ''1 + 2'' 是一个表达式
 +  * 表达式最终会产生一个值,即 ''3''
 +
 +根据操作数数量的不同,运算符可以分为:
 +
 +  * 一元运算符:只需要一个操作数,例如 ''!flag''、''typeof value''、''-num''
 +  * 二元运算符:需要两个操作数,例如 ''a + b''、''x === y''
 +  * 三元运算符:需要三个操作数,例如 ''condition ? a : b''
 +
 +本章重点讲解二元运算符和三元运算符。
 +
 +----
 +
 +===== 3.2 什么是二元运算符 =====
 +
 +二元运算符,英文通常称为 Binary Operator,是指需要两个操作数才能完成运算的运算符。
 +
 +基本格式如下:
 +
 +<code typescript>
 +左操作数 运算符 右操作数
 +</code>
 +
 +例如:
 +
 +<code typescript>
 +const sum = 10 + 20;
 +</code>
 +
 +其中:
 +
 +  * ''10'' 是左操作数
 +  * ''20'' 是右操作数
 +  * ''+'' 是二元运算符
 +
 +再比如:
 +
 +<code typescript>
 +const isAdult = age >= 18;
 +</code>
 +
 +其中:
 +
 +  * ''age'' 是左操作数
 +  * ''18'' 是右操作数
 +  * ''>='' 是二元运算符
 +  * 整个表达式的结果是一个布尔值
 +
 +TypeScript 中的大多数常用运算符都是二元运算符。例如:
 +
 +<code 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
 +</code>
 +
 +这些写法都有一个共同点:运算符左右两边各有一个表达式。
 +
 +----
 +
 +===== 3.3 二元运算符的基本特点 =====
 +
 +二元运算符有以下几个重要特点。
 +
 +第一,二元运算符必须有两个操作数。
 +
 +例如下面的代码是正确的:
 +
 +<code typescript>
 +const result = a + b;
 +</code>
 +
 +但是下面的代码是不完整的:
 +
 +<code typescript>
 +const result = a +;
 +</code>
 +
 +因为 ''+'' 作为二元运算符时,右侧缺少一个操作数。
 +
 +第二,二元运算符通常会产生一个新值。
 +
 +例如:
 +
 +<code typescript>
 +const result = 3 * 4;
 +</code>
 +
 +表达式 ''3 * 4'' 的结果是 ''12''
 +
 +第三,不同的二元运算符有不同的返回类型。
 +
 +例如:
 +
 +<code typescript>
 +const a = 1 + 2;        // number
 +const b = "a" + "b";    // string
 +const c = 10 > 5;       // boolean
 +const d = true && false; // boolean
 +</code>
 +
 +在 TypeScript 中,编译器会根据运算符和操作数类型推断表达式的结果类型。
 +
 +第四,二元运算符可能涉及类型转换。
 +
 +例如:
 +
 +<code typescript>
 +const result = "1" + 2;
 +</code>
 +
 +这里的结果不是数字 ''3'',而是字符串 ''"12"''。因为当 ''+'' 的任意一侧是字符串时,JavaScript 会倾向于执行字符串拼接。
 +
 +TypeScript 虽然增加了静态类型检查,但运行时行为仍然遵循 JavaScript。
 +
 +----
 +
 +===== 3.4 算术二元运算符 =====
 +
 +算术运算符用于进行数学计算。常见的算术二元运算符包括:
 +
 +^ 运算符 ^ 名称 ^ 示例 ^
 +| ''+'' | 加法 | ''a + b'' |
 +| ''-'' | 减法 | ''a - b'' |
 +| ''*'' | 乘法 | ''a * b'' |
 +| ''/'' | 除法 | ''a / b'' |
 +| ''%'' | 取余 | ''a % b'' |
 +| $**$ | 幂运算 | $a ** b$ |
 +
 +示例:
 +
 +<code typescript>
 +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
 +</code>
 +
 +在 TypeScript 中,这些算术运算符通常要求操作数是数字类型,或者是可以参与数值运算的类型。
 +
 +例如:
 +
 +<code typescript>
 +const x: number = 10;
 +const y: number = 5;
 +
 +const result = x - y;
 +</code>
 +
 +这是合理的。
 +
 +但是下面的代码通常不推荐:
 +
 +<code typescript>
 +const result = "10" - 5;
 +</code>
 +
 +虽然 JavaScript 运行时可能会把字符串 ''"10"'' 转成数字 ''10'',然后得到结果 ''5'',但是在 TypeScript 中应尽量避免依赖隐式类型转换。
 +
 +更推荐的写法是:
 +
 +<code typescript>
 +const value = Number("10");
 +const result = value - 5;
 +</code>
 +
 +这样代码意图更加清晰。
 +
 +----
 +
 +===== 3.5 加号运算符的特殊性 =====
 +
 +在 TypeScript 和 JavaScript 中,''+'' 是一个比较特殊的二元运算符。它既可以表示数字加法,也可以表示字符串拼接。
 +
 +数字加法:
 +
 +<code typescript>
 +const result = 1 + 2; // 3
 +</code>
 +
 +字符串拼接:
 +
 +<code typescript>
 +const message = "Hello, " + "TypeScript"; // "Hello, TypeScript"
 +</code>
 +
 +数字与字符串相加:
 +
 +<code typescript>
 +const result = "Age: " + 18; // "Age: 18"
 +</code>
 +
 +在这种情况下,数字会被转换为字符串,然后进行拼接。
 +
 +例如:
 +
 +<code typescript>
 +const a = 1 + 2 + "3";
 +console.log(a); // "33"
 +</code>
 +
 +分析过程如下:
 +
 +  * ''1 + 2'' 先执行,得到数字 ''3''
 +  * ''3 + "3"'' 再执行,因为右侧是字符串,所以执行字符串拼接
 +  * 最终结果是字符串 ''"33"''
 +
 +再看另一个例子:
 +
 +<code typescript>
 +const b = "1" + 2 + 3;
 +console.log(b); // "123"
 +</code>
 +
 +分析过程如下:
 +
 +  * ''"1" + 2'' 先执行,得到字符串 ''"12"''
 +  * ''"12" + 3'' 再执行,得到字符串 ''"123"''
 +
 +因此,在实际开发中,如果希望明确进行数字计算,应先把数据转换为数字:
 +
 +<code typescript>
 +const input = "100";
 +const result = Number(input) + 20;
 +</code>
 +
 +如果希望明确进行字符串拼接,可以使用模板字符串:
 +
 +<code typescript>
 +const name = "Alice";
 +const age = 18;
 +
 +const message = `${name} is ${age} years old.`;
 +</code>
 +
 +模板字符串通常比多个 ''+'' 拼接更清晰。
 +
 +----
 +
 +===== 3.6 赋值二元运算符 =====
 +
 +赋值运算符用于把右侧的值赋给左侧变量或属性。
 +
 +最基本的赋值运算符是:
 +
 +<code typescript>
 +=
 +</code>
 +
 +示例:
 +
 +<code typescript>
 +let count = 0;
 +count = 10;
 +</code>
 +
 +这里的 ''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'' 的近似形式 |
 +| ''??='' | 空值合并赋值 | ''a = a ?? b'' 的近似形式 |
 +
 +示例:
 +
 +<code typescript>
 +let count = 10;
 +
 +count += 5;  // 15
 +count -= 3;  // 12
 +count *= 2;  // 24
 +count /= 4;  // 6
 +count %= 4;  // 2
 +</code>
 +
 +需要注意的是,复合赋值并不总是简单的文本替换。例如:
 +
 +<code typescript>
 +obj.value += 1;
 +</code>
 +
 +大致可以理解为:
 +
 +<code typescript>
 +obj.value = obj.value + 1;
 +</code>
 +
 +但在涉及 getter、setter 或复杂属性访问时,实际求值过程可能有更细致的规则。因此,在学习阶段可以先按等价写法理解,在深入阶段再研究求值顺序。
 +
 +----
 +
 +===== 3.7 比较二元运算符 =====
 +
 +比较运算符用于比较两个值,并返回布尔值。
 +
 +常见比较运算符如下:
 +
 +^ 运算符 ^ 名称 ^ 示例 ^
 +| ''=='' | 相等 | ''a == b'' |
 +| ''!='' | 不相等 | ''a != b'' |
 +| ''==='' | 严格相等 | ''a === b'' |
 +| ''!=='' | 严格不相等 | ''a !== b'' |
 +| ''>'' | 大于 | ''a > b'' |
 +| ''<'' | 小于 | ''a < b'' |
 +| ''>='' | 大于等于 | ''a >= b'' |
 +| ''<='' | 小于等于 | ''a <= b'' |
 +
 +示例:
 +
 +<code typescript>
 +const age = 20;
 +
 +const a = age > 18;   // true
 +const b = age < 18;   // false
 +const c = age >= 20;  // true
 +const d = age <= 20;  // true
 +</code>
 +
 +在 TypeScript 中,推荐优先使用严格相等 ''==='' 和严格不相等 ''!=='',而不是 ''=='' 和 ''!=''
 +
 +原因是 ''=='' 会进行隐式类型转换。
 +
 +例如:
 +
 +<code typescript>
 +console.log(1 == "1");  // true
 +console.log(1 === "1"); // false
 +</code>
 +
 +第一行中,''=='' 会尝试进行类型转换,所以数字 ''1'' 和字符串 ''"1"'' 被认为相等。
 +
 +第二行中,''==='' 不会进行这种隐式类型转换。数字 ''1'' 和字符串 ''"1"'' 类型不同,因此结果是 ''false''
 +
 +在 TypeScript 项目中,使用 ''==='' 可以减少很多潜在错误。
 +
 +----
 +
 +===== 3.8 逻辑二元运算符 =====
 +
 +逻辑二元运算符主要包括:
 +
 +^ 运算符 ^ 名称 ^ 示例 ^
 +| ''&&'' | 逻辑与 | ''a && b'' |
 +| ''||'' | 逻辑或 | ''a || b'' |
 +
 +虽然逻辑非 ''!'' 也属于逻辑运算符,但它是一元运算符,不属于本节重点。
 +
 +逻辑与 ''&&'' 的基本规则是:
 +
 +  * 如果左侧是假值,则返回左侧
 +  * 如果左侧是真值,则返回右侧
 +
 +示例:
 +
 +<code typescript>
 +const result1 = true && "hello";  // "hello"
 +const result2 = false && "hello"; // false
 +</code>
 +
 +逻辑或 ''||'' 的基本规则是:
 +
 +  * 如果左侧是真值,则返回左侧
 +  * 如果左侧是假值,则返回右侧
 +
 +示例:
 +
 +<code typescript>
 +const result1 = "hello" || "default"; // "hello"
 +const result2 = "" || "default";      // "default"
 +</code>
 +
 +需要注意的是,''&&'' 和 ''||'' 返回的不一定是布尔值,它们返回的是其中一个操作数的值。
 +
 +例如:
 +
 +<code typescript>
 +const name = "";
 +const displayName = name || "匿名用户";
 +
 +console.log(displayName); // "匿名用户"
 +</code>
 +
 +这里 ''name'' 是空字符串,属于假值,因此 ''name || "匿名用户"'' 返回右侧的 ''"匿名用户"''
 +
 +JavaScript 中常见的假值包括:
 +
 +  * ''false''
 +  * ''0''
 +  * ''-0''
 +  * ''0n''
 +  * $''$
 +  * ''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 (
 +    <div>
 +      {isLoggedIn ? <p>欢迎回来</p> : <p>请先登录</p>}
 +    </div>
 +  );
 +}
 +</code>
 +
 +在 JSX 中,三元运算符比 ''if else'' 更常用于“根据条件渲染不同内容”的场景。
 +
 +但是,如果条件逻辑复杂,建议提前在函数体中计算好结果:
 +
 +<code tsx>
 +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>;
 +}
 +</code>
 +
 +这样 JSX 部分会更清晰。
 +
 +----
 +
 +===== 3.21 三元运算符与空值合并运算符的组合 =====
 +
 +三元运算符经常和 ''??''、''?.'' 等运算符一起使用。
 +
 +例如:
 +
 +<code typescript>
 +const displayName = user.name ?? "匿名用户";
 +</code>
 +
 +如果逻辑更复杂:
 +
 +<code typescript>
 +const displayName = user.name
 +  ? user.name
 +  : user.nickname ?? "匿名用户";
 +</code>
 +
 +这里的逻辑是:
 +
 +  * 如果 ''user.name'' 是真值,使用 ''user.name''
 +  * 否则尝试使用 ''user.nickname''
 +  * 如果 ''user.nickname'' 是 ''null'' 或 ''undefined'',使用 ''"匿名用户"''
 +
 +但要注意,''user.name ? user.name : ...'' 会把空字符串当作假值。如果空字符串也应该被认为是有效值,就应该使用 ''??''
 +
 +<code typescript>
 +const displayName = user.name ?? user.nickname ?? "匿名用户";
 +</code>
 +
 +这表示只有当前一个值为 ''null'' 或 ''undefined'' 时,才继续使用下一个值。
 +
 +----
 +
 +===== 3.22 运算符优先级简介 =====
 +
 +当一个表达式中出现多个运算符时,运算符优先级决定了先执行哪个。
 +
 +例如:
 +
 +<code typescript>
 +const result = 1 + 2 * 3;
 +</code>
 +
 +结果是 ''7'',而不是 ''9''。因为乘法 ''*'' 的优先级高于加法 ''+''
 +
 +如果希望先执行加法,可以使用括号:
 +
 +<code typescript>
 +const result = (1 + 2) * 3;
 +</code>
 +
 +这时结果是 ''9''
 +
 +在实际开发中,不建议过度依赖记忆运算符优先级。对于稍复杂的表达式,推荐使用括号明确表达意图。
 +
 +例如:
 +
 +<code typescript>
 +const result = a && b || c;
 +</code>
 +
 +可以改写为:
 +
 +<code typescript>
 +const result = (a && b) || c;
 +</code>
 +
 +或者:
 +
 +<code typescript>
 +const result = a && (b || c);
 +</code>
 +
 +根据实际需求明确分组。
 +
 +----
 +
 +===== 3.23 ?? 与 &&、|| 混用时的注意事项 =====
 +
 +空值合并运算符 ''??'' 和逻辑与 ''&&''、逻辑或 ''||'' 混用时需要特别注意。
 +
 +在 JavaScript 和 TypeScript 中,不能在没有括号的情况下直接混用 ''??'' 和 ''||'' 或 ''&&''
 +
 +例如下面的写法是错误的:
 +
 +<code typescript>
 +const value = a ?? b || c;
 +</code>
 +
 +应该使用括号明确优先级:
 +
 +<code typescript>
 +const value = (a ?? b) || c;
 +</code>
 +
 +或者:
 +
 +<code typescript>
 +const value = a ?? (b || c);
 +</code>
 +
 +这两种写法含义不同。
 +
 +第一种:
 +
 +<code typescript>
 +const value = (a ?? b) || c;
 +</code>
 +
 +表示先判断 ''a'' 是否为空值。如果 ''a'' 不是 ''null'' 或 ''undefined'',先得到 ''a'';否则得到 ''b''。然后再把这个结果与 ''c'' 做逻辑或。
 +
 +第二种:
 +
 +<code typescript>
 +const value = a ?? (b || c);
 +</code>
 +
 +表示如果 ''a'' 不是 ''null'' 或 ''undefined'',直接返回 ''a'';否则再计算 ''b || c''
 +
 +示例:
 +
 +<code typescript>
 +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
 +</code>
 +
 +分析:
 +
 +  * ''value1'' 中,''a ?? b'' 的结果是 ''0'',然后 ''0 || c'' 的结果是 ''"C"''
 +  * ''value2'' 中,''a'' 不是空值,所以 ''a ?? (b || c)'' 直接返回 ''0''
 +
 +因此混用这些运算符时,必须认真使用括号。
 +
 +----
 +
 +===== 3.24 二元运算符和三元运算符的可读性原则 =====
 +
 +运算符虽然可以让代码更简洁,但并不意味着越短越好。
 +
 +推荐原则如下:
 +
 +  * 简单表达式可以使用运算符
 +  * 复杂逻辑不要强行压缩到一行
 +  * 涉及多个运算符时优先使用括号
 +  * 三元运算符适合“二选一”赋值
 +  * 嵌套三元运算符要谨慎使用
 +  * 对默认值处理优先考虑 ''??''
 +  * 对布尔条件组合要注意短路行为
 +  * 对数字和字符串混合运算要避免隐式转换
 +
 +例如,不推荐:
 +
 +<code typescript>
 +const result = a ? b ? c : d : e ? f : g;
 +</code>
 +
 +更推荐:
 +
 +<code typescript>
 +let result: string;
 +
 +if (a) {
 +  result = b ? c : d;
 +} else {
 +  result = e ? f : g;
 +}
 +</code>
 +
 +虽然代码行数更多,但逻辑更清晰。
 +
 +----
 +
 +===== 3.25 实际开发示例:默认配置 =====
 +
 +假设有一个配置对象:
 +
 +<code typescript>
 +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
 +  };
 +}
 +</code>
 +
 +这里使用 ''??'' 而不是 ''||'' 是非常重要的。
 +
 +因为以下配置是有意义的:
 +
 +<code typescript>
 +createOptions({
 +  timeout: 0,
 +  retry: 0,
 +  debug: false
 +});
 +</code>
 +
 +如果使用 ''||''
 +
 +<code typescript>
 +const timeout = options.timeout || 3000;
 +const retry = options.retry || 3;
 +const debug = options.debug || false;
 +</code>
 +
 +那么 ''timeout: 0'' 和 ''retry: 0'' 会被错误地替换为默认值。
 +
 +使用 ''??'' 可以避免这个问题。
 +
 +----
 +
 +===== 3.26 实际开发示例:状态显示 =====
 +
 +假设一个请求状态可能是:
 +
 +<code typescript>
 +type RequestStatus = "idle" | "loading" | "success" | "error";
 +</code>
 +
 +可以用三元运算符显示文字:
 +
 +<code typescript>
 +function getStatusText(status: RequestStatus): string {
 +  return status === "loading"
 +    ? "加载中"
 +    : status === "success"
 +      ? "加载成功"
 +      : status === "error"
 +        ? "加载失败"
 +        : "等待开始";
 +}
 +</code>
 +
 +虽然这段代码可以正常工作,但嵌套三元较多。
 +
 +更推荐:
 +
 +<code typescript>
 +function getStatusText(status: RequestStatus): string {
 +  const map: Record<RequestStatus, string> = {
 +    idle: "等待开始",
 +    loading: "加载中",
 +    success: "加载成功",
 +    error: "加载失败"
 +  };
 +
 +  return map[status];
 +}
 +</code>
 +
 +如果状态值可能来自外部字符串:
 +
 +<code typescript>
 +function getStatusText(status: string): string {
 +  const map: Record<string, string> = {
 +    idle: "等待开始",
 +    loading: "加载中",
 +    success: "加载成功",
 +    error: "加载失败"
 +  };
 +
 +  return map[status] ?? "未知状态";
 +}
 +</code>
 +
 +这里 ''??'' 用来处理找不到映射结果的情况。
 +
 +----
 +
 +===== 3.27 实际开发示例:表单校验 =====
 +
 +二元运算符和三元运算符在表单校验中非常常见。
 +
 +例如:
 +
 +<code typescript>
 +function validateUsername(username: string): string {
 +  return username.length >= 3 ? "" : "用户名至少需要 3 个字符";
 +}
 +</code>
 +
 +如果有多个条件:
 +
 +<code typescript>
 +function validatePassword(password: string): string {
 +  if (password.length < 8) {
 +    return "密码至少需要 8 个字符";
 +  }
 +
 +  if (!/[A-Z]/.test(password)) {
 +    return "密码至少需要包含一个大写字母";
 +  }
 +
 +  if (!/[0-9]/.test(password)) {
 +    return "密码至少需要包含一个数字";
 +  }
 +
 +  return "";
 +}
 +</code>
 +
 +这里不建议写成复杂嵌套三元:
 +
 +<code typescript>
 +function validatePassword(password: string): string {
 +  return password.length < 8
 +    ? "密码至少需要 8 个字符"
 +    : !/[A-Z]/.test(password)
 +      ? "密码至少需要包含一个大写字母"
 +      : !/[0-9]/.test(password)
 +        ? "密码至少需要包含一个数字"
 +        : "";
 +}
 +</code>
 +
 +虽然它是合法的,但可读性不如多个 ''if''
 +
 +----
 +
 +===== 3.28 实际开发示例:权限判断 =====
 +
 +权限判断中常用逻辑二元运算符。
 +
 +例如:
 +
 +<code typescript>
 +interface User {
 +  role: "admin" | "editor" | "guest";
 +  active: boolean;
 +}
 +
 +function canEdit(user: User): boolean {
 +  return user.active && (user.role === "admin" || user.role === "editor");
 +}
 +</code>
 +
 +这里:
 +
 +  * ''user.active && ...'' 表示用户必须处于启用状态
 +  * ''user.role === "admin" || user.role === "editor"'' 表示角色必须是管理员或编辑者
 +  * 括号用于明确逻辑组合关系
 +
 +如果不用括号:
 +
 +<code typescript>
 +return user.active && user.role === "admin" || user.role === "editor";
 +</code>
 +
 +虽然按照优先级也能执行,但可读性不如带括号的版本,而且容易让读者误解。
 +
 +更清晰的写法:
 +
 +<code typescript>
 +function canEdit(user: User): boolean {
 +  const isActive = user.active;
 +  const hasRole = user.role === "admin" || user.role === "editor";
 +
 +  return isActive && hasRole;
 +}
 +</code>
 +
 +这种写法在复杂业务逻辑中更容易维护。
 +
 +----
 +
 +===== 3.29 常见错误一:把 || 当作默认值处理的一切方案 =====
 +
 +很多初学者会这样写:
 +
 +<code typescript>
 +const pageSize = inputPageSize || 20;
 +</code>
 +
 +这在某些场景下没有问题,但如果 ''inputPageSize'' 可能是 ''0'',就会出现问题。
 +
 +例如:
 +
 +<code typescript>
 +const inputPageSize = 0;
 +const pageSize = inputPageSize || 20;
 +
 +console.log(pageSize); // 20
 +</code>
 +
 +如果 ''0'' 是一个合法值,那么这里就错了。
 +
 +更推荐:
 +
 +<code typescript>
 +const pageSize = inputPageSize ?? 20;
 +</code>
 +
 +这样只有当 ''inputPageSize'' 是 ''null'' 或 ''undefined'' 时才使用默认值。
 +
 +----
 +
 +===== 3.30 常见错误二:三元运算符嵌套过深 =====
 +
 +例如:
 +
 +<code typescript>
 +const result = a
 +  ? b
 +    ? c
 +    : d
 +  : e
 +    ? f
 +    : g;
 +</code>
 +
 +这类代码虽然简短,但阅读成本较高。
 +
 +建议改写为 ''if else''
 +
 +<code typescript>
 +let result;
 +
 +if (a) {
 +  if (b) {
 +    result = c;
 +  } else {
 +    result = d;
 +  }
 +} else {
 +  if (e) {
 +    result = f;
 +  } else {
 +    result = g;
 +  }
 +}
 +</code>
 +
 +或者根据业务场景抽成函数:
 +
 +<code typescript>
 +function getResult() {
 +  if (a && b) {
 +    return c;
 +  }
 +
 +  if (a && !b) {
 +    return d;
 +  }
 +
 +  if (!a && e) {
 +    return f;
 +  }
 +
 +  return g;
 +}
 +
 +const result = getResult();
 +</code>
 +
 +函数化往往能让复杂条件更容易测试和维护。
 +
 +----
 +
 +===== 3.31 常见错误三:忽略 TypeScript 的联合类型 =====
 +
 +三元运算符可能产生联合类型。
 +
 +例如:
 +
 +<code typescript>
 +const value = condition ? "ok" : 0;
 +</code>
 +
 +此时 ''value'' 的类型可能是:
 +
 +<code typescript>
 +string | number
 +</code>
 +
 +如果后续直接调用字符串方法:
 +
 +<code typescript>
 +console.log(value.toUpperCase());
 +</code>
 +
 +TypeScript 会报错,因为 ''number'' 类型没有 ''toUpperCase'' 方法。
 +
 +正确做法是先进行类型判断:
 +
 +<code typescript>
 +if (typeof value === "string") {
 +  console.log(value.toUpperCase());
 +}
 +</code>
 +
 +或者保证三元表达式两个分支返回同一种类型:
 +
 +<code typescript>
 +const value = condition ? "ok" : "0";
 +console.log(value.toUpperCase());
 +</code>
 +
 +----
 +
 +===== 3.32 常见错误四:误解 && 和 || 的返回值 =====
 +
 +很多人以为 ''&&'' 和 ''||'' 一定返回布尔值,其实它们返回的是操作数本身。
 +
 +例如:
 +
 +<code typescript>
 +const result = "hello" && 123;
 +console.log(result); // 123
 +</code>
 +
 +再比如:
 +
 +<code typescript>
 +const result = "" || "default";
 +console.log(result); // "default"
 +</code>
 +
 +如果你需要明确得到布尔值,可以使用 ''Boolean()'' 或双重逻辑非 ''!!''
 +
 +<code typescript>
 +const value = "hello";
 +
 +const bool1 = Boolean(value);
 +const bool2 = !!value;
 +</code>
 +
 +在 TypeScript 中,清楚区分“返回某个值”和“返回布尔值”非常重要。
 +
 +----
 +
 +===== 3.33 常见错误五:对象比较使用 === 判断内容相等 =====
 +
 +比较运算符 ''==='' 比较对象时,比较的是引用地址,而不是对象内容。
 +
 +例如:
 +
 +<code typescript>
 +const a = { name: "Alice" };
 +const b = { name: "Alice" };
 +
 +console.log(a === b); // false
 +</code>
 +
 +虽然两个对象内容看起来一样,但它们是两个不同对象。
 +
 +如果是同一个引用:
 +
 +<code typescript>
 +const a = { name: "Alice" };
 +const b = a;
 +
 +console.log(a === b); // true
 +</code>
 +
 +数组也是一样:
 +
 +<code typescript>
 +console.log([1, 2] === [1, 2]); // false
 +</code>
 +
 +如果需要比较内容,需要自己逐项比较,或者使用专门的工具函数。
 +
 +简单数组比较示例:
 +
 +<code typescript>
 +function isSameNumberArray(a: number[], b: number[]): boolean {
 +  if (a.length !== b.length) {
 +    return false;
 +  }
 +
 +  return a.every((item, index) => item === b[index]);
 +}
 +</code>
 +
 +----
 +
 +===== 3.34 本章小结 =====
 +
 +本章详细介绍了 TypeScript 中的二元运算符与三元运算符。
 +
 +二元运算符是需要两个操作数的运算符,例如:
 +
 +<code typescript>
 +a + b
 +a === b
 +a && b
 +a ?? b
 +</code>
 +
 +常见二元运算符包括:
 +
 +  * 算术运算符
 +  * 赋值运算符
 +  * 比较运算符
 +  * 逻辑运算符
 +  * 空值合并运算符
 +  * 位运算符
 +  * 关系运算符
 +
 +三元运算符是需要三个操作数的运算符。TypeScript 中最常见的三元运算符是条件运算符:
 +
 +<code typescript>
 +condition ? valueIfTrue : valueIfFalse
 +</code>
 +
 +它适合根据条件选择一个值,但不适合承载过于复杂的业务逻辑。
 +
 +在实际开发中,应特别注意以下几点:
 +
 +  * ''??'' 是空值合并运算符,也是二元运算符
 +  * ''??'' 只在左侧为 ''null'' 或 ''undefined'' 时使用右侧
 +  * ''||'' 会在左侧是假值时使用右侧
 +  * ''&&'' 和 ''||'' 不一定返回布尔值
 +  * 三元运算符适合简单二选一
 +  * 嵌套三元运算符要谨慎使用
 +  * 比较对象时,''==='' 比较的是引用,而不是内容
 +  * 复杂表达式建议使用括号提高可读性
 +  * TypeScript 会根据运算符推断表达式类型
 +  * 当三元运算符两个分支类型不同,结果通常是联合类型
 +
 +理解这些规则之后,就能更准确地阅读和编写 TypeScript 表达式,也能避免许多由隐式类型转换、默认值处理、逻辑短路和类型推断造成的常见错误。

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。