TypeScript 类型收窄机制深度解析

TypeScript 类型收窄机制深度解析

TypeScript TypeScript 使用手册(中文版)翻译。http://www.typescriptlang.org TypeScript 项目地址: https://gitcode.com/gh_mirrors/typ/TypeScript

引言

在 TypeScript 开发中,类型收窄(Type Narrowing)是一项核心特性,它允许我们基于代码逻辑将宽泛的类型转换为更具体的类型。本文将全面解析 TypeScript 中的类型收窄机制,帮助开发者编写更安全、更精确的类型代码。

基础概念

类型收窄是指 TypeScript 编译器能够根据代码中的特定条件判断,自动将变量的类型范围缩小到更具体的子类型。这种机制使得我们可以在不同的代码分支中使用不同类型的属性和方法。

基本示例

考虑以下函数:

function padLeft(padding: number | string, input: string): string {
  if (typeof padding === 'number') {
    return ' '.repeat(padding) + input;  // 这里padding被识别为number类型
  }
  return padding + input;  // 这里padding被识别为string类型
}

在这个例子中,typeof padding === 'number' 是一个类型守卫(Type Guard),它告诉 TypeScript 编译器在这个分支中 padding 一定是 number 类型。

类型守卫详解

TypeScript 支持多种类型的类型守卫,每种都有其特定的使用场景。

1. typeof 类型守卫

typeof 是最常用的类型守卫之一,它可以识别以下基本类型:

  • "string"
  • "number"
  • "bigint"
  • "boolean"
  • "symbol"
  • "undefined"
  • "object"
  • "function"

需要注意的是,typeof null 也会返回 "object",这是 JavaScript 的历史遗留问题。

2. 真值检查

在 JavaScript 中,以下值会被视为假值(falsy):

  • 0
  • NaN
  • ""(空字符串)
  • 0n(BigInt 零)
  • null
  • undefined

利用这一点,我们可以进行真值检查来排除 nullundefined

function printAll(strs: string | string[] | null) {
  if (strs && typeof strs === 'object') {
    // strs 是 string[]
  } else if (typeof strs === 'string') {
    // strs 是 string
  }
}

3. 等式检查

使用 ===!====!= 也可以进行类型收窄:

function example(x: string | number, y: string | boolean) {
  if (x === y) {
    // x 和 y 都是 string 类型
  }
}

4. in 操作符

in 操作符可以检查对象是否包含特定属性:

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    return animal.swim();  // animal 是 Fish 类型
  }
  return animal.fly();    // animal 是 Bird 类型
}

5. instanceof 检查

instanceof 用于检查对象是否是某个类的实例:

function logValue(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());  // x 是 Date 类型
  } else {
    console.log(x.toUpperCase());   // x 是 string 类型
  }
}

控制流分析

TypeScript 的控制流分析能够跟踪代码中变量的类型变化:

function example() {
  let x: string | number | boolean;
  
  x = Math.random() < 0.5;  // x 是 boolean
  console.log(x);
  
  if (Math.random() < 0.5) {
    x = 'hello';  // x 是 string
    console.log(x);
  } else {
    x = 100;      // x 是 number
    console.log(x);
  }
  
  return x;       // x 是 string | number
}

自定义类型守卫

我们可以创建自定义的类型守卫函数:

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

let pet = getSmallPet();
if (isFish(pet)) {
  pet.swim();  // pet 是 Fish 类型
} else {
  pet.fly();   // pet 是 Bird 类型
}

可辨识联合类型

可辨识联合(Discriminated Unions)是一种强大的模式,它通过共同的字面量属性来区分联合类型中的不同成员:

interface Circle {
  kind: 'circle';
  radius: number;
}

interface Square {
  kind: 'square';
  sideLength: number;
}

type Shape = Circle | Square;

function getArea(shape: Shape) {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.sideLength ** 2;
  }
}

完备性检查与 never 类型

never 类型表示不应该存在的状态,可用于确保所有可能情况都被处理:

function getArea(shape: Shape) {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.sideLength ** 2;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

如果添加新的 Shape 类型而没有更新 switch 语句,TypeScript 会报错。

最佳实践

  1. 优先使用可辨识联合而不是可选属性
  2. 利用控制流分析简化代码
  3. 使用 never 类型进行完备性检查
  4. 为复杂逻辑创建自定义类型守卫
  5. 避免过度使用类型断言(!as

结语

TypeScript 的类型收窄机制提供了强大的类型安全性,同时保持了 JavaScript 的灵活性。通过合理运用各种类型守卫和控制流分析,开发者可以编写出既安全又易于维护的代码。理解并掌握这些概念是成为 TypeScript 高级开发者的关键一步。

TypeScript TypeScript 使用手册(中文版)翻译。http://www.typescriptlang.org TypeScript 项目地址: https://gitcode.com/gh_mirrors/typ/TypeScript

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董洲锴Blackbird

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值