一、交叉类型与联合类型的本质区别

在TypeScript中,交叉类型(Intersection Types)和联合类型(Union Types)是两种常见的类型组合方式,但它们的行为截然不同。

交叉类型使用 & 符号,将多个类型合并为一个新类型,新类型会包含所有成员。例如:

// 技术栈:TypeScript
interface Person {
  name: string;
  age: number;
}

interface Employee {
  company: string;
  role: string;
}

// 交叉类型:同时具备Person和Employee的所有属性
type EmployeePerson = Person & Employee;

const john: EmployeePerson = {
  name: "John",
  age: 30,
  company: "TechCorp",
  role: "Developer"
};
// 注释:john必须包含name、age、company、role四个属性

联合类型使用 | 符号,表示一个值可以是几种类型之一。例如:

type StringOrNumber = string | number;

function printId(id: StringOrNumber) {
  console.log(id);
}
printId(101);     // 合法
printId("ABC101"); // 合法
// printId(true);  // 报错:boolean不在联合类型中

关键区别

  • 交叉类型是“合并”,要求同时满足所有类型约束。
  • 联合类型是“或”,只需满足其中任意一种类型约束。

二、类型断言的安全边界

类型断言(Type Assertion)允许开发者手动指定值的类型,但滥用会导致运行时错误。TypeScript提供了两种语法:

const userInput: unknown = "Hello World";

// 语法1:尖括号(不推荐在JSX中使用)
const str1 = <string>userInput;
// 语法2:as关键字(推荐)
const str2 = userInput as string;

安全边界问题

interface Cat {
  meow(): void;
}
interface Dog {
  bark(): void;
}

function isCat(animal: Cat | Dog): animal is Cat {
  return "meow" in animal;
}

const pet: Cat | Dog = { bark: () => console.log("Woof!") };

if (isCat(pet)) {
  pet.meow(); // 类型收窄为Cat
} else {
  pet.bark(); // 类型收窄为Dog
}
// 注释:通过类型守卫(Type Guard)避免不安全断言

最佳实践

  1. 优先使用类型守卫(如 typeofinstanceof、自定义函数)。
  2. 避免对 anyunknown 直接断言,除非明确知道运行时类型。

三、映射类型的灵活应用

映射类型(Mapped Types)允许基于旧类型创建新类型,常见于工具类型(Utility Types)如 PartialReadonly

基础示例

type Keys = "name" | "age";
type Person = {
  [K in Keys]: string;
};
// 等价于 { name: string; age: string; }

进阶应用

interface User {
  id: number;
  name: string;
  password: string;
}

// 剔除password字段,生成新类型
type SafeUser = Omit<User, "password">;
// 等价于 { id: number; name: string; }

// 将所有字段改为可选
type PartialUser = Partial<User>;
// 等价于 { id?: number; name?: string; password?: string; }

关联技术:结合 keyof 实现动态映射:

type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};
const nullableUser: Nullable<User> = {
  id: null,
  name: null,
  password: "123456" // 仍允许非null
};

四、应用场景与注意事项

  1. 交叉类型:适合合并多个接口或扩展第三方库类型。

    type ReactProps = Props & State; // 合并React组件props和state
    
  2. 联合类型:适合处理异构数据(如API响应)。

    type APIResponse = SuccessResponse | ErrorResponse;
    
  3. 类型断言:谨慎用于DOM操作或遗留代码迁移。

    const input = document.getElementById("user") as HTMLInputElement;
    
  4. 映射类型:适合批量修改属性(如生成表单验证规则)。

注意事项

  • 避免深度嵌套的交叉类型,可能导致类型推断性能下降。
  • 联合类型需用类型守卫收窄范围,否则只能访问共有成员。

总结

交叉类型与联合类型分别解决“合并”和“选择”问题,类型断言需在安全边界内使用,而映射类型能大幅提升类型复用性。掌握这些特性后,可以更灵活地设计复杂类型系统。