前置知识: TypeScript

映射类型与键重映射

2 minAdvanced2026/6/14

TypeScript映射类型与键重映射详解:Mapped Types、as子句与高级模式。

1. 映射型基础

1.1 基本语法

type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

type Optional<T> = {
  [K in keyof T]?: T[K];
};

interface User {
  name: string;
  age: number;
}

type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number }

1.2 映射型与索引签名

// 从联合类型创建对象类型
type Flags = { [K in 'read' | 'write' | 'execute']: boolean };
// { read: boolean; write: boolean; execute: boolean }

2. 键重映射(Key Remapping)

2.1 as 子句

TypeScript 4.1 引入 as 子句,允许在映射时转换键名:

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

interface User {
  name: string;
  age: number;
}

type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }

2.2 过滤键

type RemoveNull<T> = {
  [K in keyof T as T[K] extends null | undefined ? never : K]: T[K];
};

type User = { name: string; age: null; email: string | undefined };
type CleanUser = RemoveNull<User>;
// { name: string; email: string | undefined }

2.3 键名转换

// 添加前缀
type Prefix<T, P extends string> = {
  [K in keyof T as `${P}${Capitalize<string & K>}`]: T[K];
};

type PrefixedUser = Prefix<User, 'user'>;
// { userName: string; userAge: number }

3. 内置映射

说明
Partial<T>所有属性可选
Required<T>所有属性必需
Readonly<T>所有属性只读
Pick<T, K>选取部分属性
Omit<T, K>排除部分属性
Record<K, V>构建键值对
Exclude<U, E>联合类型排除
Extract<U, E>联合类型提取
NonNullable<T>排除 null/undefined

4. 高级映射模式

4.1 深层 Partial

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

4.2 值型映射

type MapValues<T, F extends (v: any) => any> = {
  [K in keyof T]: F extends (v: T[K]) => infer R ? R : never;
};

4.3 条件映射

type ConditionalPick<T, Condition> = {
  [K in keyof T as T[K] extends Condition ? K : never]: T[K];
};

type StringProps = ConditionalPick<User, string>; // { name: string }