Skip to content

TypeScript 高阶用法全解析

一、核心要点速览

💡 核心考点

  • 泛型进阶:泛型约束、默认参数、泛型工具函数
  • 条件类型:infer 推断、分布式条件类型
  • 映射类型:in 操作符、as 重映射、修饰符
  • 工具类型:Pick、Omit、Partial、Required 等实现原理
  • 类型体操:常用类型技巧与实战应用

二、泛型进阶

1. 泛型约束 (Generic Constraints)

typescript
// 基础约束:extends 关键字
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // ✅ 可以访问 length
  return arg;
}

loggingIdentity("hello"); // ✅ string 有 length
loggingIdentity([1, 2, 3]); // ✅ array 有 length
loggingIdentity(123); // ❌ number 没有 length

多约束条件:

typescript
// 多个约束的联合类型
function merge<T extends object, U extends object>(
  objA: T,
  objB: U
): T & U {
  return Object.assign(objA, objB);
}

const result = merge({ name: "Alice" }, { age: 25 });
// 类型:{ name: string } & { age: number }

使用 keyof 约束:

typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person = { name: "John", age: 30 };
getProperty(person, "name"); // ✅
getProperty(person, "email"); // ❌ 'email' 不在 keyof T 中

2. 泛型默认参数

typescript
interface Response<T = any> {
  data: T;
  status: number;
  message: string;
}

// 使用默认值
const res1: Response = { data: null, status: 200, message: "OK" };

// 指定具体类型
interface ApiResponse<T = unknown> {
  code: number;
  result: T;
}

type UserResponse = ApiResponse<{ id: number; name: string }>;

3. 泛型工具函数

typescript
// 泛型数组操作
function filterTruthy<T>(arr: (T | null | undefined)[]): T[] {
  return arr.filter((item): item is T => item != null);
}

filterTruthy([1, null, 2, undefined, 3]); // [1, 2, 3]

// 泛型 Promise 处理
function unwrapPromise<T>(promise: Promise<T>): Promise<T> {
  return promise.then((res) => res);
}

// 泛型事件处理
class EventEmitter<T extends Record<string, any>> {
  private listeners: {
    [K in keyof T]?: ((data: T[K]) => void)[];
  } = {};

  on<K extends keyof T>(event: K, callback: (data: T[K]) => void) {
    this.listeners[event]?.push(callback);
  }

  emit<K extends keyof T>(event: K, data: T[K]) {
    this.listeners[event]?.forEach((cb) => cb(data));
  }
}

// 使用
const emitter = new EventEmitter<{
  login: { userId: number };
  logout: void;
}>();
emitter.on("login", (data) => console.log(data.userId));

三、条件类型 (Conditional Types)

1. 基础语法

typescript
// T extends U ? X : Y
type IsString<T> = T extends string ? true : false;

type A = IsString<"hello">; // true
type B = IsString<123>; // false

2. infer 关键字 - 类型推断

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

type FnReturn = ReturnType<() => string>; // string

// 推断数组元素类型
type ElementType<T> = T extends (infer U)[] ? U : T;

type ArrElem = ElementType<string[]>; // string

// 推断 Promise 内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type PromiseValue = UnwrapPromise<Promise<number>>; // number

// 推断函数参数类型
type FirstParameter<T> = T extends (
  arg1: infer P,
  ...args: any[]
) => any
  ? P
  : never;

type Param = FirstParameter<(name: string, age: number) => void>; // string

3. 分布式条件类型

typescript
// 当 T 是联合类型时,条件类型会分发到每个成员
type ToArray<T> = T extends any ? T[] : never;

type Result = ToArray<string | number>;
// 等价于:ToArray<string> | ToArray<number>
// 结果:string[] | number[]

// 实际应用:提取联合类型的字面量
type GetValues<T> = T extends { value: infer V } ? V : never;

type Options = 
  | { type: "text"; value: string }
  | { type: "number"; value: number }
  | { type: "boolean"; value: boolean };

type ValueTypes = GetValues<Options>; // string | number | boolean

4. 实用条件类型示例

typescript
// 判断是否为 any
type IsAny<T> = 0 extends 1 & T ? true : false;

type A = IsAny<any>; // true
type B = IsAny<string>; // false

// 判断是否为 never
type IsNever<T> = [T] extends [never] ? true : false;

type C = IsNever<never>; // true
type D = IsNever<string>; // false

// 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;

type E = NonNullable<string | null | undefined>; // string

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

type FnParams = Parameters<(name: string, age: number) => void>;
// [string, number]

四、映射类型 (Mapped Types)

1. 基础映射类型

typescript
// 将属性变为可选
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// 将属性变为必填
type Required<T> = {
  [P in keyof T]: T[P];
};

// 将属性变为只读
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

// 示例
interface User {
  name: string;
  age: number;
}

type PartialUser = Partial<User>; // { name?: string; age?: number }
type RequiredUser = Required<User>; // { name: string; age: number }

2. 键的重映射 (Key Remapping)

typescript
// TypeScript 4.1+ 特性
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 PropertiesWithoutId<T> = {
  [K in keyof T as Exclude<K, "id">]: T[K];
};

interface Entity {
  id: number;
  name: string;
  email: string;
}

type EntityWithoutId = PropertiesWithoutId<Entity>;
// { name: string; email: string }

// 只保留特定类型
type ExtractStringKeys<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface Mixed {
  name: string;
  age: number;
  email: string;
  active: boolean;
}

type StringOnly = ExtractStringKeys<Mixed>;
// { name: string; email: string }

3. 值类型转换

typescript
// 将所有属性转为字符串
type Stringify<T> = {
  [K in keyof T]: string;
};

type UserString = Stringify<{ name: string; age: number }>;
// { name: string; age: string }

// 嵌套对象的深度字符串化
type DeepStringify<T> = {
  [K in keyof T]: T[K] extends object
    ? DeepStringify<T[K]>
    : string;
};

interface Nested {
  user: {
    name: string;
    profile: {
      age: number;
    };
  };
  status: boolean;
}

type DeepString = DeepStringify<Nested>;
// { user: { name: string; profile: { age: string } }; status: string }

4. 组合映射类型

typescript
// 组合 Partial 和 Readonly
type ReadonlyPartial<T> = {
  readonly [P in keyof T]?: T[P];
};

// 组合 Pick 和 Readonly
type ReadonlyPick<T, K extends keyof T> = {
  readonly [P in K]: T[P];
};

// 实际应用:不可变的更新函数参数
type ImmutableUpdate<T> = {
  readonly [P in keyof T]?: T[P] | ((prev: T[P]) => T[P]);
};

function updateState<T>(
  state: T,
  updates: ImmutableUpdate<T>
): T {
  const result = { ...state };
  
  for (const key in updates) {
    const value = updates[key];
    if (typeof value === "function") {
      result[key] = value(result[key]);
    } else {
      result[key] = value;
    }
  }
  
  return result;
}

五、内置工具类型详解

1. 属性操作类

typescript
// Pick - 选择部分属性
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;
// { title: string; completed: boolean }

// Omit - 排除部分属性
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

type TodoInfo = Omit<Todo, "completed">;
// { title: string; description: string }

// Extract - 提取匹配的类型
type Extract<T, U> = T extends U ? T : never;

type ValidEvents = Extract<"click" | "scroll" | "error", "click" | "scroll">;
// "click" | "scroll"

// Exclude - 排除匹配的类型
type Exclude<T, U> = T extends U ? never : T;

type RemainingEvents = Exclude<"click" | "scroll" | "error", "click">;
// "scroll" | "error"

2. 可空性操作类

typescript
// Partial - 全部可选
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// Required - 全部必填
type Required<T> = {
  [P in keyof T]-?: T[P];
};

// Readonly - 全部只读
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

// Mutable - 移除 readonly
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

// ReadonlyArray - 只读数组
type ReadonlyArray<T> = readonly T[];

// 示例
interface Config {
  debug: boolean;
  timeout: number;
}

type OptionalConfig = Partial<Config>; // { debug?: boolean; timeout?: number }
type StrictConfig = Required<Config>; // { debug: boolean; timeout: number }

3. 函数相关类

typescript
// Parameters - 获取参数类型
type Parameters<T extends (...args: any[]) => any> = 
  T extends (...args: infer P) => any ? P : never;

type FnParams = Parameters<(a: string, b: number) => void>;
// [string, number]

// ConstructorParameters - 获取构造函数参数
type ConstructorParameters<
  T extends abstract new (...args: any[]) => any
> = T extends abstract new (...args: infer P) => any ? P : never;

class MyClass {
  constructor(public name: string, public age: number) {}
}

type ClassParams = ConstructorParameters<typeof MyClass>;
// [string, number]

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

type FnReturn = ReturnType<() => Promise<string>>;
// Promise<string>

// InstanceType - 获取实例类型
type InstanceType<T extends abstract new (...args: any[]) => any> = 
  T extends abstract new (...args: any[]) => infer R ? R : any;

type MyInstance = InstanceType<typeof MyClass>;
// MyClass

// ThisParameterType - 获取 this 类型
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any
  ? U
  : unknown;

// OmitThisParameter - 移除 this 参数
type OmitThisParameter<T> = unknown extends ThisParameterType<T>
  ? T
  : T extends (...args: infer A) => infer R
    ? (...args: A) => R
    : T;

4. 其他工具类型

typescript
// Record - 构建对象类型
type Record<K extends keyof any, T> = {
  [P in K]: T;
};

type PageInfo = Record<"home" | "about" | "contact", { title: string }>;
// { home: { title: string }; about: { title: string }; contact: { title: string } }

// NonNullable - 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;

type A = NonNullable<string | null | undefined>; // string

// Capitalize/Uncapitalize - 首字母大小写转换
type Greeting = Capitalize<"hello">; // "Hello"
type LowerGreeting = Uncapitalize<"Hello">; // "hello"

// Awaited - Promise 解包
type PromiseResult = Awaited<Promise<string>>; // string
type NestedPromise = Awaited<Promise<Promise<number>>>; // number

六、类型体操实战

1. 字符串操作类型

typescript
// 字符串拼接
type Concat<T extends string, U extends string> = `${T}${U}`;

type Result = Concat<"Hello", "World">; // "HelloWorld"

// 转大写
type Uppercase<T extends string> = /* intrinsic */ T;

// 自定义模板类型
type EventName<T extends string> = `on${Capitalize<T>}Change`;

type NameChange = EventName<"user">; // "onUserChange"

// 分割字符串
type Split<S extends string, D extends string> = 
  S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S];

type Parts = Split<"a-b-c", "-">; // ["a", "b", "c"]

// 连接数组
type Join<T extends string[], D extends string> = 
  T extends [infer F extends string, ...infer R extends string[]]
    ? R extends []
      ? F
      : `${F}${D}${Join<R, D>}`
    : never;

type Path = Join<["api", "users", "123"], "/">; // "api/users/123"

2. 数字操作类型

typescript
// 递增
type Increment<N extends number> = 
  [...TupleOf<N>, 1]["length"] & number;

type TupleOf<N extends number, T extends any[] = []> = 
  T["length"] extends N ? T : TupleOf<N, [...T, any]>;

type Next = Increment<5>; // 6

// 递减(简化版)
type Decrement<N extends number> = 
  N extends 0 ? 0 : 
  [...TupleOf<N>]["length"] & number;

// 加法
type Add<A extends number, B extends number> = 
  [...TupleOf<A>, ...TupleOf<B>]["length"] & number;

type Sum = Add<3, 5>; // 8

3. 数组操作类型

typescript
// 第一个元素
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;

type Head = First<[string, number, boolean]>; // string

// 最后一个元素
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;

type Tail = Last<[string, number, boolean]>; // boolean

// 移除第一个
type DropFirst<T extends any[]> = T extends [any, ...infer R] ? R : never;

type Rest = DropFirst<[string, number, boolean]>; // [number, boolean]

// 移除最后一个
type DropLast<T extends any[]> = T extends [...infer I, any] ? I : never;

type Init = DropLast<[string, number, boolean]>; // [string, number]

// 数组长度的类型
type Length<T extends readonly any[]> = T["length"];

type Len = Length<[1, 2, 3]>; // 3

4. 对象深度操作

typescript
// 深度 Partial
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

interface Nested {
  user: {
    name: string;
    profile: {
      age: number;
    };
  };
}

type DeepOpt = DeepPartial<Nested>;
// { user?: { name?: string; profile?: { age?: number } } }

// 深度 Readonly
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// 按路径获取类型
type GetPath<T, P extends string> = 
  P extends `${infer K}.${infer R}`
    ? K extends keyof T
      ? GetPath<T[K], R>
      : never
    : P extends keyof T
      ? T[P]
      : never;

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

type Host = GetPath<Config, "database.host">; // string

5. 函数柯里化类型

typescript
// 柯里化函数类型
type Curry<T extends (...args: any[]) => any> = 
  T extends (arg: infer A) => infer R
    ? (arg: A) => Curry<R>
    : ReturnType<T>;

declare function add(a: number): (b: number) => (c: number) => number;

type CurriedAdd = Curry<typeof add>;
// (a: number) => (b: number) => (c: number) => number

// 参数反转
type ReverseParams<T extends any[]> = 
  T extends [infer F, ...infer R]
    ? [...ReverseParams<R>, F]
    : [];

type Reversed = ReverseParams<[string, number, boolean]>;
// [boolean, number, string]

七、高级模式与技巧

1. 标签模板类型

typescript
// 类型安全的 API 调用
type ApiPaths = `/api/${string}`;

function fetchApi<T>(path: ApiPaths): Promise<T> {
  return fetch(path).then(res => res.json());
}

// ✅ 安全
fetchApi<{ users: string[] }>("/api/users");
// ❌ 错误
fetchApi("/users");

2. 品牌类型 (Branded Types)

typescript
// 创建品牌类型
type UserId = string & { readonly brand: unique symbol };
type Email = string & { readonly brand: unique symbol };

function createUserId(id: string): UserId {
  return id as UserId;
}

function validateEmail(email: string): Email {
  if (!email.includes("@")) throw new Error("Invalid email");
  return email as Email;
}

// 类型安全
const userId: UserId = createUserId("123");
const email: Email = validateEmail("[email protected]");

// ❌ 不能混用
function sendEmail(to: Email) {}
sendEmail(userId); // 类型错误

3. 精确对象类型

typescript
// 禁止多余属性
type Exact<T, Shape extends T> = T & 
  Record<Exclude<keyof Shape, keyof T>, never>;

function processUser(user: Exact<{ name: string }, { name: string; age?: number }>) {
  return user;
}

processUser({ name: "John" }); // ✅
processUser({ name: "John", age: 30 }); // ✅
processUser({ name: "John", extra: "field" }); // ❌ 类型错误

4. 条件推断的高级应用

typescript
// 自动推断 React Hook 类型
type HookReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 推断组件 Props
type ComponentProps<T> = T extends React.ComponentType<infer P> ? P : never;

// 推断 Zod Schema 类型
type InferZodType<T> = T extends z.ZodType<infer U> ? U : never;

5. 递归类型限制解决方案

typescript
// TypeScript 限制递归深度为 50
// 使用尾递归优化

// ❌ 可能导致递归过深
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;

// ✅ 使用辅助类型
type FlattenHelper<T, Acc extends any[] = []> = 
  T extends [infer F, ...infer R]
    ? FlattenHelper<R, [...Acc, Flatten<F>]>
    : Acc;

type Flatten<T> = FlattenHelper<T>[number];

八、实战应用场景

1. API 响应类型处理

typescript
// 统一的 API 响应包装
type ApiResponse<T> = {
  code: number;
  data: T;
  message: string;
};

// 提取数据类型
type Data<T> = T extends ApiResponse<infer U> ? U : never;

// 处理分页响应
type PaginatedResponse<T> = ApiResponse<{
  items: T[];
  total: number;
  page: number;
}>;

// 使用
type UserList = PaginatedResponse<{ id: number; name: string }>;

2. 表单验证类型

typescript
// 验证规则定义
type ValidationRule<T> = {
  required?: boolean;
  pattern?: RegExp;
  validator?: (value: T) => boolean;
  message?: string;
};

// 表单配置类型
type FormConfig<T> = {
  [K in keyof T]: ValidationRule<T[K]>;
};

// 表单数据状态
type FormState<T> = {
  values: T;
  errors: Partial<Record<keyof T, string>>;
  touched: Partial<Record<keyof T, boolean>>;
};

3. Redux/Vuex 类型增强

typescript
// Action 类型定义
type Action<T extends string, P = void> = {
  type: T;
  payload: P;
};

// Reducer 类型
type Reducer<S, A extends { type: string }> = 
  (state: S, action: A) => S;

// Store 类型
type Store<S, A extends { type: string }> = {
  state: S;
  dispatch: (action: A) => void;
  subscribe: (listener: () => void) => () => void;
};

// 使用示例
type UserAction = 
  | Action<"LOGIN", { username: string }>
  | Action<"LOGOUT">;

type UserState = { isLoggedIn: boolean; username?: string };

4. 数据库模型映射

typescript
// Prisma 风格类型
type ModelFields = {
  id: { type: "Int"; primary: true; autoincrement: true };
  name: { type: "String"; nullable: false };
  email: { type: "String"; nullable: true };
};

// 从 Schema 推断类型
type InferModel<T extends Record<string, ModelFields>> = {
  [K in keyof T]: T[K]["nullable"] extends true 
    ? T[K]["type"] extends "Int" ? number | null
    : T[K]["type"] extends "String" ? string | null
    : never
    : T[K]["type"] extends "Int" ? number
    : T[K]["type"] extends "String" ? string
    : never;
};

type User = InferModel<{
  id: { type: "Int"; primary: true; autoincrement: true };
  name: { type: "String"; nullable: false };
  email: { type: "String"; nullable: true };
}>;
// { id: number; name: string; email: string | null }

九、常见面试题

Q1: 解释一下 infer 关键字的作用?

回答要点:

  1. infer 用于条件类型中的类型推断
  2. 通常在 extends 语句的条件分支中使用
  3. 可以提取函数返回值、数组元素、Promise 内部类型等
  4. 示例:type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any

Q2: 什么是分布式条件类型?有什么特点?

回答要点:

  • 当条件类型的检查对象是联合类型时,会自动分发到每个成员
  • 形式:T extends U ? X : Y,其中 T 是裸类型参数
  • 特点:分别对联合类型每个成员应用条件,最后合并结果
  • 用途:批量类型转换、过滤等

Q3: 如何实现一个 Omit 类型?

回答要点:

typescript
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
  1. 使用 Exclude 从 T 的键中排除 K
  2. 使用 Pick 选择剩余的键
  3. 核心是组合使用工具类型

Q4: 映射类型中的 as 有什么用?

回答要点:

  • TypeScript 4.1+ 引入的键重映射功能
  • 可以在映射时修改键名
  • 示例:[K in keyof T as \get${K}`]: T[K]`
  • 可以配合 extends 进行条件映射

Q5: 如何编写类型安全的 API 客户端?

回答要点:

  1. 使用模板字符串类型约束路径
  2. 使用泛型定义响应类型
  3. 使用条件类型推断返回数据
  4. 使用品牌类型防止类型混淆
  5. 示例代码展示完整实现

十、记忆口诀

📝 快速记忆

TypeScript 高阶口诀:

  • 泛型约束用 extends,infer 推断藏条件
  • 映射类型用 in 遍历,as 重映射改键名
  • 条件分发联合类型,工具类型常组合
  • Pick Omit 互为实现,Partial Required 相反用
  • 类型体操练推理,实战应用更熟练

十一、延伸学习

推荐资源

下一步建议

最近更新