高级类型

TypeScript 的高级类型系统提供了强大的类型操作能力,包括联合类型、交叉类型、条件类型、映射类型等。


联合类型(Union Types)

联合类型表示值可以是多种类型之一。

基本语法

type ID = string | number;
 
function printId(id: ID): void {
  console.log(`ID: ${id}`);
}
 
printId(101);     // ✅
printId("abc");   // ✅

类型收窄

function process(value: string | number): string {
  // 类型收窄
  if (typeof value === "string") {
    return value.toUpperCase(); // value 是 string
  }
  return value.toFixed(2); // value 是 number
}

可辨识联合(Discriminated Unions)

interface Circle {
  kind: "circle";
  radius: number;
}
 
interface Rectangle {
  kind: "rectangle";
  width: number;
  height: number;
}
 
interface Triangle {
  kind: "triangle";
  base: number;
  height: number;
}
 
type Shape = Circle | Rectangle | Triangle;
 
function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rectangle":
      return shape.width * shape.height;
    case "triangle":
      return (shape.base * shape.height) / 2;
  }
}

交叉类型(Intersection Types)

交叉类型将多个类型合并为一个。

interface Name {
  firstName: string;
  lastName: string;
}
 
interface Age {
  age: number;
}
 
type Person = Name & Age;
 
const person: Person = {
  firstName: "John",
  lastName: "Doe",
  age: 30
};

合并接口

interface Printable {
  print(): void;
}
 
interface Loggable {
  log(): void;
}
 
type PrintableLoggable = Printable & Loggable;
 
const obj: PrintableLoggable = {
  print() { console.log("printing"); },
  log() { console.log("logging"); }
};

与联合类型结合

type SuccessResponse = {
  status: "success";
  data: unknown;
};
 
type ErrorResponse = {
  status: "error";
  message: string;
};
 
type Response = SuccessResponse | ErrorResponse;
 
// 带有通用属性的响应
type TimestampedResponse = Response & { timestamp: Date };

条件类型(Conditional Types)

条件类型根据条件选择类型。

基本语法

type IsString<T> = T extends string ? true : false;
 
type A = IsString<string>;  // true
type B = IsString<number>;  // false

实用示例

// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
 
function greet(): string {
  return "hello";
}
 
type GreetReturn = ReturnType<typeof greet>; // string
 
// 提取 Promise 解析类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
 
type A = Awaited<Promise<string>>;  // string
type B = Awaited<Promise<number>>;  // number
type C = Awaited<string>;           // string

infer 关键字

infer 用于在条件类型中推断类型:

// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
 
type A = ElementType<string[]>;   // string
type B = ElementType<number[]>;   // number
 
// 提取函数第一个参数类型
type FirstArg<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;
 
type C = FirstArg<(name: string, age: number) => void>; // string
 
// 提取 Promise 类型
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;
 
type D = UnwrapPromise<Promise<Promise<string>>>; // string

分布式条件类型

当条件类型作用于联合类型时,会自动分布:

type ToArray<T> = T extends any ? T[] : never;
 
type Result = ToArray<string | number>;
// string[] | number[](而不是 (string | number)[])
 
// 阻止分布
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Result2 = ToArrayNonDist<string | number>;
// (string | number)[]

映射类型(Mapped Types)

映射类型基于现有类型创建新类型。

基本语法

type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};
 
type Partial<T> = {
  [K in keyof T]?: T[K];
};
 
interface User {
  name: string;
  age: number;
}
 
type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number }
 
type PartialUser = Partial<User>;
// { name?: string; age?: number }

键名重映射(Key Remapping)

TypeScript 4.1+ 支持使用 as 重映射键名:

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
 
interface Person {
  name: string;
  age: number;
}
 
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }

过滤属性

// 移除指定属性
type RemoveKind<T> = {
  [K in keyof T as Exclude<K, "kind">]: T[K];
};
 
// 只保留函数属性
type FunctionsOnly<T> = {
  [K in keyof T as T[K] extends Function ? K : never]: T[K];
};
 
interface Example {
  name: string;
  greet(): void;
  calculate(x: number): number;
}
 
type ExampleFunctions = FunctionsOnly<Example>;
// { greet: () => void; calculate: (x: number) => number }

修饰符操作

// 移除 readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};
 
// 移除可选
type Required<T> = {
  [K in keyof T]-?: T[K];
};
 
// 添加可选
type Optional<T> = {
  [K in keyof T]+?: T[K];
};

模板字面量类型(Template Literal Types)

TypeScript 4.1+ 支持模板字面量类型。

基本语法

type World = "world";
type Greeting = `hello ${World}`; // "hello world"
 
type EmailLocale = "en" | "zh" | "ja";
type EmailEvent = "welcome" | "goodbye";
type EmailId = `${EmailLocale}_${EmailEvent}`;
// "en_welcome" | "en_goodbye" | "zh_welcome" | "zh_goodbye" | "ja_welcome" | "ja_goodbye"

内置字符串操作类型

type Upper = Uppercase<"hello">;     // "HELLO"
type Lower = Lowercase<"HELLO">;     // "hello"
type Cap = Capitalize<"hello">;      // "Hello"
type Uncap = Uncapitalize<"Hello">; // "hello"

实用模式

// CSS 属性名
type CSSProperty = `${string}-${string}`;
const prop: CSSProperty = "font-size"; // ✅
 
// 事件处理器
type EventHandler<T extends string> = `on${Capitalize<T>}`;
type ClickHandler = EventHandler<"click">; // "onClick"
 
// API 路由
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type Route = "/users" | "/posts";
type Endpoint = `${HTTPMethod} ${Route}`;

索引访问类型(Indexed Access Types)

interface User {
  name: string;
  age: number;
  address: {
    city: string;
    country: string;
  };
}
 
// 访问单个属性类型
type UserName = User["name"]; // string
 
// 访问嵌套属性类型
type City = User["address"]["city"]; // string
 
// 联合索引
type NameOrAge = User["name" | "age"]; // string | number
 
// 使用 keyof
type UserValues = User[keyof User]; // string | number | { city: string; country: string }

数组索引类型

const colors = ["red", "green", "blue"] as const;
 
type Color = typeof colors[number]; // "red" | "green" | "blue"
 
// 元组索引
type Tuple = [string, number, boolean];
type First = Tuple[0];  // string
type Second = Tuple[1]; // number

类型操作工具

keyof 操作符

interface User {
  name: string;
  age: number;
  email: string;
}
 
type UserKeys = keyof User; // "name" | "age" | "email"
 
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

typeof 操作符

const config = {
  url: "https://api.example.com",
  timeout: 5000
};
 
type Config = typeof config;
// { url: string; timeout: number }
 
// 函数类型
function greet(name: string): string {
  return `Hello, ${name}`;
}
 
type GreetFn = typeof greet;
// (name: string) => string

组合使用

const StatusCode = {
  OK: 200,
  NotFound: 404,
  ServerError: 500
} as const;
 
type StatusCodeKey = keyof typeof StatusCode;
// "OK" | "NotFound" | "ServerError"
 
type StatusCodeValue = typeof StatusCode[StatusCodeKey];
// 200 | 404 | 500

实用类型模式

深度 Partial

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
 
interface Config {
  server: {
    host: string;
    port: number;
  };
  database: {
    url: string;
  };
}
 
type PartialConfig = DeepPartial<Config>;
// 所有嵌套属性都变为可选

深度 Readonly

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

路径类型

type PathKeys<T, Prefix extends string = ""> = T extends object
  ? {
      [K in keyof T]: K extends string
        ? Prefix extends ""
          ? K | `${K}.${PathKeys<T[K], "">}`
          : `${Prefix}.${K}` | `${Prefix}.${K}.${PathKeys<T[K], "">}`
        : never;
    }[keyof T]
  : never;

最佳实践

  1. 使用可辨识联合:处理复杂状态时优先使用
  2. 避免过度嵌套:条件类型不要嵌套太深
  3. 利用内置工具类型:不要重复造轮子
  4. 类型测试:使用 type-fest 等库验证类型
  5. 保持可读性:复杂类型使用类型别名分解
// 推荐:分解复杂类型
type ExtractString<T> = T extends string ? T : never;
type ExtractFunction<T> = T extends Function ? T : never;
 
type StringsAndFunctions<T> = ExtractString<T> | ExtractFunction<T>;
 
// 不推荐:一行写完所有逻辑
type Complex<T> = T extends string ? T : T extends Function ? T : never;

typescript 高级类型 类型系统