什么是 TypeScript 的双问号语法 - Nullish Coalescing 操作符

举报
汪子熙 发表于 2024/11/01 19:00:12 2024/11/01
【摘要】 TypeScript 是一门由 Microsoft 开发并维护的编程语言,它在 JavaScript 的基础上增加了静态类型定义,旨在提升大型应用程序的开发效率和代码质量。在 TypeScript 中,双问号语法 ??(又称为 Nullish Coalescing 操作符)是一个常用的语法特性,它旨在简化对 null 和 undefined 的处理。让我们详细介绍这一语法特性,以及它在实际应...

TypeScript 是一门由 Microsoft 开发并维护的编程语言,它在 JavaScript 的基础上增加了静态类型定义,旨在提升大型应用程序的开发效率和代码质量。在 TypeScript 中,双问号语法 ??(又称为 Nullish Coalescing 操作符)是一个常用的语法特性,它旨在简化对 null 和 undefined 的处理。让我们详细介绍这一语法特性,以及它在实际应用中的使用场景,并通过具体代码示例进一步说明。

Nullish Coalescing 操作符 ??

Nullish Coalescing 操作符 ?? 是一种逻辑操作符,它可以用于在两个表达式之间进行选择。它会返回第一个操作数的值,除非这个值是 null 或 undefined;在这种情况下,它会返回第二个操作数的值。这种处理方式对于简化 null 和 undefined 的判定非常有用,尤其在涉及到默认值的场景时。

使用场景

处理默认值

在开发过程中,我们经常会遇到这样一种情景:需要为一个可能是 null 或 undefined 的变量提供一个默认值。如果这个变量是 null 或 undefined,则使用默认值;否则,使用变量的原值。在没有 Nullish Coalescing 操作符之前,常用的做法是使用逻辑或操作符 ||,但这样会有一些潜在问题。

考虑下面的例子:

let userInput: string | null = null;
let defaultText = "Default Text";
let text = userInput || defaultText;
console.log(text); // 输出: "Default Text"

userInput = "";
text = userInput || defaultText;
console.log(text); // 输出: "Default Text" 但我们期望的是: ""

在这个例子中,即使 userInput 是一个空字符串 "",由于 "" 被认为是 falsy 值,所以 || 操作符会返回 defaultText,这与预期不符。然而,使用 Nullish Coalescing 操作符会解决这一问题:

let userInput: string | null = null;
let defaultText = "Default Text";
let text = userInput ?? defaultText;
console.log(text); // 输出: "Default Text"

userInput = "";
text = userInput ?? defaultText;
console.log(text); // 输出: ""

在这个例子中,空字符串不再被视为 null 或 undefined,所以我们得到了预期的结果。

配合可选链运算符

在处理嵌套对象时,可选链运算符 ?. 非常有用,它可以简化对象属性的访问,并有效防止 undefined 属性访问错误。结合 Nullish Coalescing 操作符,可以进一步增强代码的健壮性。

例如,有一个嵌套对象,其中某些属性可能不存在:

let person: { name?: string, address?: { city?: string } } = {};
let city = person.address?.city ?? "Unknown City";
console.log(city); // 输出: "Unknown City"

在这一示例中,如果 person.addressperson.address.cityundefinedcity 将被设置为 Unknown City。这种写法极大地简化了代码,同时避免了冗长的 null 和 undefined 检查。

其他常见使用场景

参数默认值

在函数参数处理中,通过 Nullish Coalescing 操作符为参数设置默认值是一个常见的用法:

function greet(name?: string) {
    let greeting = "Hello, " + (name ?? "Guest");
    console.log(greeting);
}

greet(); // 输出: "Hello, Guest"
greet("Alice"); // 输出: "Hello, Alice"

这个示例展示了如何优雅地为函数参数设置默认值,使得函数更加健壮和易于使用。

处理复杂的数据结构

在处理从 API 或数据库获取的复杂数据结构时,经常需要处理可能为 null 或 undefined 的情况。Nullish Coalescing 操作符在这种场景下非常有用:

type ApiResponse = {
  data?: {
    user?: {
      id?: number,
      name?: string
    }
  }
};

let response: ApiResponse = { data: { user: { id: 123 } } };
let userName = response.data?.user?.name ?? "Anonymous";
console.log(userName); // 输出: "Anonymous"

response = { data: { user: { id: 123, name: "John" } } };
userName = response.data?.user?.name ?? "Anonymous";
console.log(userName); // 输出: "John"

深入原理

Nullish Coalescing 操作符的设计理念是为了弥补逻辑或操作符 || 的不足。在 JavaScript 中,逻辑或操作符会返回第一个 truthy 值,这会将 nullundefined 和其他 falsy 值(如 0NaN、空字符串 "" 等)等同对待。然而,这种处理方式在某些情况下是不合适的,尤其是当你想明确区分 nullundefined 与其他 falsy 值的时候。

Nullish Coalescing 操作符不同于 ||,它仅在操作数是 nullundefined 时才会返回右侧操作数的值:

console.log(0 ?? 10); // 输出: 0
console.log(false ?? true); // 输出: false
console.log('' ?? 'Default'); // 输出: ''
console.log(null ?? 'Default'); // 输出: 'Default'
console.log(undefined ?? 'Default'); // 输出: 'Default'

这使得 ?? 成为一个更加贴合真实需求的运算符,尤其是在需要严格区分 nullundefined 的情况下。

注意事项

不可与逻辑或操作符混用

虽然 Nullish Coalescing 操作符和逻辑或操作符在某些情况下可以互换使用,但它们的行为是不同的。因此,在同一个表达式中不应混用这两种操作符,否则会引发 SyntaxError:

let value = null;
let result = value ?? "default" || "fallback"; // 报错: SyntaxError

为了避免这种错误,应拆分操作,确保使用的是期望的逻辑:

let value = null;
let result = (value ?? "default") || "fallback";
console.log(result); // 输出: "default"

运算顺序

Nullish Coalescing 操作符的优先级低于某些其他运算符,如赋值操作符。因此,在复合表达式中,注意运算符的优先级,或通过括号明确运算顺序:

let value = null;
let result = value ?? "default" + " value"; // 不符合预期
console.log(result); // 输出: "null"

result = (value ?? "default") + " value"; // 符合预期
console.log(result); // 输出: "default value"

更复杂的示例

为了更好地展示 Nullish Coalescing 操作符的应用场景,让我们看看一些更复杂的例子,这些例子往往存在于实际项目中。

阅读配置文件

处理从配置文件读取的值,如果某些配置项不存在,则提供默认值:

interface Config {
  host?: string;
  port?: number;
  debugMode?: boolean;
}

function readConfig(config: Config) {
  const host = config.host ?? "localhost";
  const port = config.port ?? 8080;
  const debugMode = config.debugMode ?? false;

  console.log(`Host: ${host}`);
  console.log(`Port: ${port}`);
  console.log(`Debug Mode: ${debugMode}`);
}

const config: Config = { port: 3000 };
readConfig(config);
// 输出:
// Host: localhost
// Port: 3000
// Debug Mode: false

在这个示例中,通过 Nullish Coalescing 操作符处理不存在的配置项,并为这些项提供默认值。

表单输入验证

在处理表单数据时,根据用户输入的值,进行必要的验证和数据处理:

interface FormData {
  name?: string;
  age?: number;
}

function handleFormData(formData: FormData) {
  const name = formData.name ?? "Anonymous";
  const age = formData.age ?? 0;

  console.log(`Name: ${name}, Age: ${age}`);
}

const formData: FormData = { age: 25 };
handleFormData(formData);
// 输出: Name: Anonymous, Age: 25

handleFormData({ name: "Alice" });
// 输出: Name: Alice, Age: 0

handleFormData({});
// 输出: Name: Anonymous, Age: 0

这个示例展示了如何处理表单数据中可能为空的字段,并为这些字段提供合理的默认值。

数据展示

在用户界面开发中,往往需要展示从后端获取的可能包含空值的数据:

interface User {
  firstName?: string;
  lastName?: string;
  email?: string;
}

function renderUserProfile(user: User) {
  const firstName = user.firstName ?? "N/A";
  const lastName = user.lastName ?? "N/A";
  const email = user.email ?? "Not provided";

  console.log(`Name: ${firstName} ${lastName}`);
  console.log(`Email: ${email}`);
}

const user = { firstName: "John", email: "john@example.com" };
renderUserProfile(user);
// 输出:
// Name: John N/A
// Email: john@example.com

通过 Nullish Coalescing 操作符,确保在数据缺失时可以提供默认值,从而提高用户界面的友好性和一致性。

上述示例展示了 Nullish Coalescing 操作符在处理默认值、配合可选链运算符、参数默认值、处理复杂的数据结构、配置文件读取、表单输入验证和数据展示等多个方面的应用。

通过这些具体的示例,我们可以看到 Nullish Coalescing 操作符极大地简化了代码中 null 和 undefined 的处理逻辑,使得代码更加简洁、优雅和易于维护。同时,它也帮助开发者在逻辑运算中避免了一些常见的陷阱和错误,提高了代码的健壮性和可靠性。

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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