关于 TypeScript 的 ??= 操作

举报
汪子熙 发表于 2025/06/02 13:23:26 2025/06/02
【摘要】 TypeScript 是 JavaScript 的超集,它为 JavaScript 增加了类型系统和现代语言特性,从而提升了代码的可读性和安全性。在 TypeScript 版本 4.0 之后,加入了一种新的运算符 ??=,这种运算符结合了 Nullish Coalescing 的特性以及赋值操作。这种操作符对于编写简洁和防止空值错误的代码非常有用,本文将详细介绍 ??= 的定义、使用场景、逻...

TypeScript 是 JavaScript 的超集,它为 JavaScript 增加了类型系统和现代语言特性,从而提升了代码的可读性和安全性。在 TypeScript 版本 4.0 之后,加入了一种新的运算符 ??=,这种运算符结合了 Nullish Coalescing 的特性以及赋值操作。这种操作符对于编写简洁和防止空值错误的代码非常有用,本文将详细介绍 ??= 的定义、使用场景、逻辑推理和一些示例,以帮助更好地理解这个特性。

??= 运算符的定义

??= 运算符是 TypeScript 中的一个复合赋值运算符,它的作用是当左侧的变量值为 nullundefined 时,才对其进行赋值。这个运算符基于 JavaScript 的 Nullish Coalescing 特性 (??) 和赋值操作符 (=) 的组合,允许开发者在保证左侧变量存在值时不改变它,否则对其进行新值的赋值。

具体来说,a ??= b 的逻辑含义是:

  • 如果 a 当前为 nullundefined,那么将 b 赋值给 a
  • 如果 a 不是 nullundefined,则 a 保持原样,不发生变化。

这与传统的逻辑或赋值运算符 ||= 的区别在于,??= 只在 nullundefined 时才赋值,而 ||= 则会在左侧变量是任何假值(例如 nullundefined0、空字符串 ''false)时进行赋值。

使用场景分析

在日常编程中,经常会遇到变量未初始化的情况。未初始化的变量在 JavaScript 和 TypeScript 中通常会是 undefined,在某些情况下也可能是 null。在处理这些变量时,开发者往往希望在它们未定义或为空时为它们赋值,而在它们已有意义值时保持不变。

这时候,??= 运算符就变得非常有用。通过 ??=,可以避免显式检查是否为 nullundefined 的冗余代码,从而使代码更加清晰易懂。

举个例子,设想我们在构建一个配置对象时,希望为某个参数提供默认值,而又不能覆盖已有的合法配置。这种情况就非常适合使用 ??= 运算符。

与其他运算符的区别

要更好地理解 ??=,可以将它与常见的 ||= 运算符进行对比:

  1. ||= 运算符会在左侧变量为 falsy 值时进行赋值,falsy 值包括:false0、空字符串 ''NaNnullundefined
  2. ??= 运算符则只在左侧变量为 nullundefined 时才赋值。

这样的差异使得 ??= 更加精确,因为它不会对其他 falsy 值(如 0 或空字符串)进行不必要的覆盖。这对于需要保留这些 falsy 值的场景来说尤其重要。

逻辑推理及代码示例

接下来,通过严谨的逻辑推理来一步步理解 ??= 的用法。

基本逻辑推导

假设有一个变量 a,我们希望给它赋值一个默认值 b,但仅在 a 没有值时进行。这里的“没有值”特指 anullundefined

如果不用 ??= 运算符,通常我们会这么写:

a = a ?? b;

这段代码的含义是:如果 anullundefined,则将 b 赋值给 a。这种写法虽然有效,但显得有些繁琐,需要写两次变量名。??= 运算符的出现使得这种场景变得更加简洁。

使用 ??= 运算符

通过使用 ??=,上面的代码可以简化为:

a ??= b;

这行代码的含义与前面的 a = a ?? b; 完全相同,但更加简洁,减少了重复,代码的可读性也得到了提高。

示例 1:基础用例

考虑以下代码:

let x: number | undefined;
x ??= 10;
console.log(x); // 输出:10

在这段代码中,变量 x 初始化为 undefined,而 x ??= 10 的含义是:如果 xnullundefined,则将 10 赋值给 x。因此,最终 x 的值为 10

如果我们修改 x 的初始值,使其不再是 nullundefined,则不会发生赋值:

let y: number = 5;
y ??= 10;
console.log(y); // 输出:5

因为 y 的初始值是 5,并不是 nullundefined,因此 ??= 运算符不会对它进行赋值,y 的值保持不变。

示例 2:与 ||= 的对比

let a: number = 0;
a ||= 42;
console.log(a); // 输出:42

在这段代码中,a 的初始值是 0,因为 0 在 JavaScript 中是 falsy 值,所以 ||= 运算符会将 42 赋值给 a。输出的结果是 42

然而,如果使用 ??= 运算符:

let b: number = 0;
b ??= 42;
console.log(b); // 输出:0

这里,b 的值仍然是 0,因为 ??= 运算符只会在 bnullundefined 时才进行赋值,0 并不属于这两种情况,因此 b 保持原值。

示例 3:对象属性的默认值

??= 也可以用于对象的属性赋值,尤其是在处理复杂对象配置时,确保某些属性有默认值。

interface Config {
  timeout?: number;
  retries?: number;
}

let config: Config = {};
config.timeout ??= 3000;
config.retries ??= 5;

console.log(config); // 输出:{ timeout: 3000, retries: 5 }

在这段代码中,config 对象最初是空的,没有定义 timeoutretries 属性。通过 ??= 运算符,可以为这两个属性赋予默认值 30005。这样,如果对象在某些部分没有初始化,开发者可以通过 ??= 来确保有默认的配置值。

如果 config 已经定义了一些属性值:

let config2: Config = { timeout: 1000 };
config2.timeout ??= 3000;
config2.retries ??= 5;

console.log(config2); // 输出:{ timeout: 1000, retries: 5 }

在这个例子中,config2timeout 已经被设置为 1000,所以 ??= 运算符不会对它重新赋值。而 retries 尚未定义,因此它被赋值为 5

??= 的适用场景

为了更好地理解何时应使用 ??=,可以考虑如下场景:

  1. 变量未初始化的处理:当变量可能未被初始化为有效值时,使用 ??= 可以确保它们得到合理的默认值,而不会影响已有的合法值。

  2. 配置对象的默认值:在为配置对象中的属性赋值时,??= 可以确保在属性未定义的情况下为它们设置默认值,而不会覆盖已经提供的有效配置。

  3. 减少重复代码:在代码中直接使用 ??= 可以减少重复引用变量名的情况,使代码更加简洁和易读。

例如,假设我们有一个函数来处理用户传入的选项对象:

function processOptions(options?: { delay?: number }) {
  options = options || {};
  options.delay ??= 100;
  console.log(`Delay is: ${options.delay}`);
}

processOptions(); // 输出:Delay is: 100
processOptions({ delay: 200 }); // 输出:Delay is: 200

在这里,通过 ??=,我们确保 delay 属性总是有一个默认值 100,但不会覆盖用户传入的自定义值 200

与空值合并运算符 (??) 的联系

??= 运算符是基于空值合并运算符 (??) 的一种简化形式,因此理解 ??= 的前提是对 ?? 有一定的了解。空值合并运算符 ?? 用于在操作中处理可能为 nullundefined 的值,并在这些值为 nullundefined 时提供一个默认值。例如:

let result = value ?? defaultValue;

这行代码的意思是:如果 valuenullundefined,那么 result 的值就是 defaultValue,否则就是 value

??= 可以看作是 ?? 与赋值运算符的组合,使得在满足条件时直接对变量进行赋值,而不需要显式写出赋值逻辑。

代码精简与可读性提升

编写代码时,开发者经常强调代码的可读性和简洁性。传统的写法如 a = a ?? b,虽然功能上可以满足需求,但在大型项目中可能显得冗长,尤其是需要多次重复时。通过 ??= 运算符,开发者可以使用更加精炼的代码来实现相同的逻辑。

考虑以下代码片段:

function initializeSettings(settings?: { theme?: string; fontSize?: number }) {
  settings = settings || {};
  settings.theme ??= 'light';
  settings.fontSize ??= 14;
  console.log(settings);
}

initializeSettings(); // 输出:{ theme: 'light', fontSize: 14 }
initializeSettings({ theme: 'dark' }); // 输出:{ theme: 'dark', fontSize: 14 }

通过使用 ??=,代码的逻辑变得更加直观,去掉了不必要的重复。此外,如果代码逻辑相对复杂,??= 可以帮助减少对某个变量多次引用的错误风险。

结论与应用建议

??= 运算符为 TypeScript 提供了一种简洁而强大的方式来处理空值赋值的问题。它的主要优势在于可以有效防止 nullundefined 的错误,同时避免覆盖其他有效的 falsy 值,如 0 和空字符串。它在设置默认值、处理配置对象、初始化变量等场景中都有广泛的应用。

为了更好地利用 ??=,建议开发者在以下场景中使用它:

  • 当变量可能是 nullundefined,并且需要提供默认值时,使用 ??= 可以减少代码量并提升可读性。
  • 在配置和初始化对象时,使用 ??= 可以有效确保所有必需的属性都有值,同时不会覆盖用户的自定义设置。
  • 尽量避免在需要处理 falsy 值的场景中使用 ||=,使用 ??= 可以更精确地控制逻辑流程。

通过对 ??= 运算符的充分理解和合理应用,可以在 TypeScript 开发中编写更加健壮和简洁的代码。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。