正在加载,请稍候…

TypeScript 高级类型:泛型、条件类型与模板字面量类型

掌握 TypeScript 高级类型系统:带约束的泛型、条件类型、映射类型、模板字面量类型和 infer 关键字,附真实示例。

TypeScript 高级类型:泛型、条件类型与模板字面量类型

为什么高级 TypeScript 类型很重要

TypeScript 的类型系统是图灵完备的——它可以表达你能想象到的几乎任何约束。这里介绍的高级特性让你能够编写既灵活又精确类型的 API,在编译时而非运行时捕获整类错误。

TypeScript 高级类型:泛型、条件类型与模板字面量类型 插图

带约束的泛型

// 基本泛型
function identity<T>(value: T): T {
  return value;
}

// 带约束的泛型——T 必须具有 length 属性
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}

longest("hello", "world");  // ✅ 字符串有 .length
longest([1, 2, 3], [1, 2]); // ✅ 数组有 .length
longest(1, 2);               // ❌ 错误:数字没有 .length

// 多个类型参数
function zip<T, U>(arr1: T[], arr2: U[]): [T, U][] {
  return arr1.map((item, i) => [item, arr2[i]]);
}

const pairs = zip([1, 2, 3], ['a', 'b', 'c']);
// pairs: [number, string][]

条件类型

根据条件分支的类型:

// 基本条件类型
type IsString<T> = T extends string ? 'yes' : 'no';

type A = IsString<string>;   // 'yes'
type B = IsString<number>;   // 'no'
type C = IsString<string | number>; // 'yes' | 'no'(分发!)

// Extract/Exclude(内置条件类型)
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;

type Status = 'pending' | 'active' | 'deleted';
type ActiveStatus = Exclude<Status, 'deleted'>; // 'pending' | 'active'

// NonNullable
type NonNullable<T> = T extends null | undefined ? never : T;
type Defined = NonNullable<string | null | undefined>; // string

TypeScript 高级类型:泛型、条件类型与模板字面量类型 插图

infer 关键字

从泛型结构中提取类型信息:

// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function fetchUser(): Promise<{ id: string; name: string }> { ... }
type UserResponse = ReturnType<typeof fetchUser>; 
// UserResponse = Promise<{ id: string; name: string }>

// 解包 Promise
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type User = Awaited<Promise<Promise<{ id: string }>>>;
// User = { id: string }

// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

function createUser(name: string, age: number, role: 'admin' | 'user') {}
type CreateUserParams = Parameters<typeof createUser>;
// [string, number, 'admin' | 'user']

映射类型

系统地转换对象类型:

// 使所有属性可选
type Partial<T> = { [K in keyof T]?: T[K] };

// 使所有属性必填
type Required<T> = { [K in keyof T]-?: T[K] };

// 使所有属性只读
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// 自定义映射:使所有属性可为 null
type Nullable<T> = { [K in keyof T]: T[K] | null };

// 转换值类型
type Stringify<T> = { [K in keyof T]: string };

// 选择键的子集
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

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

type PublicUser = Omit<User, 'password'>;
// { id: string; name: string; email: string }

type UserPreview = Pick<User, 'id' | 'name'>;
// { id: string; name: string }

TypeScript 高级类型:泛型、条件类型与模板字面量类型 插图

模板字面量类型

在类型层面操作字符串类型:

type EventName = 'click' | 'focus' | 'blur';
type EventHandler = `on${Capitalize<EventName>}`;
// 'onClick' | 'onFocus' | 'onBlur'

// API 路由类型
type HttpMethod = 'get' | 'post' | 'put' | 'delete';
type Endpoint = '/users' | '/orders' | '/products';
type ApiRoute = `${Uppercase<HttpMethod>} ${Endpoint}`;
// 'GET /users' | 'GET /orders' | ...(所有组合)

// 类型安全的 CSS 属性名
type CSSProperty = 'margin' | 'padding' | 'border';
type CSSDirection = 'top' | 'right' | 'bottom' | 'left';
type DirectionalProperty = `${CSSProperty}-${CSSDirection}`;
// 'margin-top' | 'margin-right' | ... 等等

// 对象键推断
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

interface Config {
  host: string;
  port: number;
}

type ConfigGetters = Getters<Config>;
// { getHost: () => string; getPort: () => number }

可辨识联合

对类型安全的变体进行模式匹配:

type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

async function fetchUser(id: string): Promise<Result<User>> {
  try {
    const user = await db.users.findById(id);
    return { success: true, data: user };
  } catch (error) {
    return { success: false, error: error as Error };
  }
}

const result = await fetchUser('123');

if (result.success) {
  console.log(result.data.name); // TypeScript 知道 data 存在
} else {
  console.error(result.error.message); // TypeScript 知道 error 存在
}

// 穷举匹配
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; side: number }
  | { kind: 'triangle'; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle': return Math.PI * shape.radius ** 2;
    case 'square': return shape.side ** 2;
    case 'triangle': return 0.5 * shape.base * shape.height;
    default:
      // 如果你添加了新的 Shape 变体而未处理,此行会报错
      const _exhaustive: never = shape;
      throw new Error(`Unhandled: ${_exhaustive}`);
  }
}

工具类型模式

// 深度可选(递归地使所有属性可选)
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

// 递归只读
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

// 将联合类型展平为交叉类型
type UnionToIntersection<U> = 
  (U extends any ? (x: U) => void : never) extends (x: infer I) => void 
    ? I 
    : never;

// 使特定键必填,其余可选
type RequiredKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;

interface CreateUserDto {
  name?: string;
  email?: string;
  role?: string;
}
type ValidCreateUserDto = RequiredKeys<CreateUserDto, 'name' | 'email'>;
// { name: string; email: string; role?: string }

→ 使用 JSON Viewer 查看和检查复杂的 TypeScript 数据结构。