TypeScript联合类型实战:从基础运算到类型收窄全解析

作者:函数茶馆

时间:2026-10-30

浏览:435

本文深入解析TypeScript中的联合类型(Union Types)及其实际应用。内容涵盖联合类型的基础运算、使用typeof和instanceof进行类型收窄的局限性,以及TypeScript特有的类型谓词和可辨别联合类型(Discriminated Unions)的高级用法。通过具体代码示例,详细阐述如何区分any与unknown,帮助开发者更精准地处理复杂类型场景。

文章目录

TypeScript的类型系统提供了多种运算方式,用于处理复杂的数据结构。

联合类型可以直观地理解为集合的并集操作。例如,定义类型A1为number,类型B1为string,那么类型C1即为A1与B1的并集(A1 | B1)。变量c1被赋予数值42,符合该联合类型定义。

type A1 = number;
type B1 = string;
type C1 = A1 | B1;
const c1: C1 = 42;
// 这种可以理解成集合的并集,但是他们没有交集
type A2 = { name: string };
// 这里要理解成 A2表示name为string的所有对象,比如{name: 'a', age: 14}, 它既可以有age也可以没age
type B2 = { age: number };
type C2 = A2 | B2;
const c2: C2 = {
  name: 'John',
  age: 18,
};
// 这种可以理解成两个集合的并集,并且他们有交集

二. 联合类型的实际应用方法

在使用联合类型时,必须通过特定手段将类型范围缩小(Narrowing),否则编译器仅允许调用这些类型共有的方法或属性。

这种将联合类型缩小到具体子类型的过程,在TypeScript中被称为类型收窄(Narrowing)。

const f1 = (a: number | string) => {
  if (typeof a === 'number') {
    a.toFixed();
  } else {
    a.split(',');
  }
};

三. typeof 操作符的局限

typeof运算符在处理数组、普通对象、Date对象以及null时,统一返回'object'字符串,这限制了其区分具体对象类型的能力。

四. 利用 instanceof 进行类型区分

const f1 = (a: Date | Date[]) => {
  if (a instanceof Date) {
    a;
  } else {
    a;
  }
};

五. instanceof 方法的不足

instanceof运算符存在明显缺陷,它无法用于string、number、boolean等基础基本类型的类型检查。

为克服单一运算符的局限,通常将instanceof与typeof结合使用,以覆盖更广泛的类型区分场景。

const f1 = (a: Date | string | Date[]) => {
  if (typeof a === 'string') {
    a;
  } else if (a instanceof Date) {
    a;
  } else {
    a;
  }
};

此外,instanceof无法识别TypeScript独有的类型定义,因为那些类型在编译为JavaScript后会被擦除,导致运行时无法保留类型信息。

使用in运算符可以解决部分区分问题,但它并非万能,例如无法有效区分两个具有不同实现但结构相似的函数类型。

type Person = {
  name: string;
};
type Animal = {
  x: string;
};
const f1 = (a: Person | Animal) => {
  if('name' in a) {
    a
  } else if ('x' in a) {
    a
  } else {
    a
  }
}

TypeScript官方文档中推荐了多种类型收窄方案,开发者应根据具体场景选择合适的方法。

六. TypeScript 中区分类型的多种策略

前述方法主要基于JavaScript原有的类型检查机制,接下来我们将探讨TypeScript特有的类型区分与收窄策略。

类型谓词(Type Predicate)使用is关键字进行判断。其优势在于支持所有TypeScript类型,但缺点是语法相对繁琐,需要显式定义返回类型。

type Rect = {
  height: number;
  width: number;
};
type Circle = {
  center: [number, number];
  radius: number;
};
const f1 = (a: Rect | Circle) => {
  if (isRect(a)) {
    a;
  }
};
// 这里x is Rect就是类型谓词,用于收窄类型,写boolean不行,因为这里写boolean的话ts不知道boolean的含义是什么
function isRect(x: Rect | Circle): x is Rect {
  return 'height' in x && 'width' in x;
}

使用可辨别联合类型(Discriminated Union)并通过特定字段进行标识,可以将复杂类型的收窄逻辑简化为对简单类型的比较,提高代码可读性和安全性。

  • 使用可辨别联合类型的条件包括:联合类型T由A、B、C、D等多个子类型组成。

这些子类型A、B、C、D...必须包含一个相同的属性,通常命名为kind或其他标识符。

该标识属性的类型必须是简单类型(如字符串字面量或数字字面量)。

各个子类型中该标识属性的值必须互不相同,从而能够唯一区分每个类型。

总结而言,我们需要一个同名的、可辨别的简单类型key,具备这些特征的联合类型T被称为可辨别联合类型。

type Circle = { kind: 'Circle'; center: [number, number] };
type Square = { kind: 'Square'; sideLength: number };
// 两个联合起来的类型通过加上kind字段来进行判断类型
type Shape = Circle | Square;
const f1 = (a: string | number | Shape) => {
  if (typeof a === 'string') {
    a;
  } else if (typeof a === 'number') {
    a;
  } else if (a.kind === 'Circle') {
    a;
  } else {
    a;
  }
};

下面补充一个使用可辨别联合类型的典型应用场景。

在React开发中,我们经常需要处理用户的增删改查操作,此时可使用可辨别联合类型来定义不同的Action对象。

type Action =
  | { type: 'getUser'; id: string }
  | { type: 'createUser'; attributes: any }
  | { type: 'deleteUser'; id: string }
  | { type: 'updateUser'; attributes: any };

除了上述方法,还可以使用类型断言(Type Assertion)来强制指定类型。

type Circle = { kind: 'Circle'; center: [number, number] };
type Square = { kind: 'Square'; x: number };
type Shape = Circle | Square;
const f1 = (a: Shape) => {
  (a as Circle).center; // 这就是断言
};
f1({ kind: 'Circle', center: [1, 2] });

七. any 是否涵盖所有类型(排除 never/unknown/any/void)?原因解析

any并非所有类型(除了never、unknown、any、void)的联合。原因在于,一旦两个具体类型联合,编译器仅允许调用它们共有的方法;而any类型可以调用所有类型的方法,突破了类型检查的限制。

const f1 = (a: number | string) => {
  a.toFixed(); // 会报错
  a.split(); // 会报错
  a.toString(); // 只能用number和string共有的方法
};
const f2 = (a: any) => {
  a.toFixed(); // 不会报错
  a.split(); // 不会报错
};

any类型在某种程度上类似于“法外狂徒”,TypeScript的绝大多数类型检查规则对any类型不生效,这可能导致运行时错误。

const f1 = (a: any) => {
  const b: never = a; // 会报错,这算是any唯一不能违反的规则
};

那么,什么类型才是除了never、unknown、any、void之外的所有类型的联合呢?

答案是unknown。因为unknown类型可以收窄到任何具体类型,它代表了所有类型的联合,是类型安全的any。

const f1 = (a: unknown) => {
 if(a instanceof Date) {
   a // Date
 }
 if(isPerson(a)) {
   a // Person
 }
}

以上即为TypeScript中联合类型的详细使用示例与解析,更多关于TypeScript联合类型的资料请关注本站其它相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章