Kodokon kodokon.com

映射类型与键重映射:变换类型

理解同态映射类型如何保留修饰符,以及 as 子句如何重命名或过滤键。

8 分钟 · 3 题

在 Kodokon 中打开本课

映射类型遍历一组键来产生一个新的对象类型。[K in keyof T] 这种形式被称为同态的:编译器知道结构来自 T,并会自动复制每个原始属性的 readonly? 修饰符。+- 前缀让你能够显式地添加或移除这些修饰符:这正是 PartialRequiredReadonly 背后的内部机制。

TYPESCRIPT
type Mutable<T> = {
  -readonly [K in keyof T]-?: T[K];
};

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

type Draft = Mutable<Config>;
// { host: string; port: number }
- 前缀移除了 readonly 和可选性。

自 TypeScript 4.1 起,as 子句支持键重映射:每个键都可以即时重命名,通常通过模板字面量类型来完成。而如果该子句产生 never,这个键就会直接从结果中被移除:这就是官方的属性过滤机制。注意示例中的 & stringkeyof T 可能包含 symbolnumber 类型的键,它们与模板字面量不兼容。

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

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

type OnlyMethods<T> = {
  [K in keyof T as
    T[K] extends (...args: never[]) => unknown
      ? K : never]: T[K];
};
用 as 重命名,通过产生 never 来过滤。

一个常被忽视的规范细节:当应用于数组或元组时,同态映射类型不会产生一个带有 01length 键的对象。编译器会保留类型的本质:它变换元素,并保留数组或元组的形状。这就是 Readonly<[number, string]> 能保持为元组的原因。一旦映射类型不再是同态的,例如使用 [P in K](其中 K 是任意的键联合类型),这种保留就会消失。

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

type Pair = Immutable<[number, string]>;
// readonly [number, string]: the tuple survives

type List = Immutable<string[]>;
// readonly string[]
同态映射类型尊重数组和元组。

知识检测

确认你已牢记本课的重点内容。

  1. 在键重映射中,当 as 子句对某个键产生 never 时会发生什么?
    • 该属性获得 never 类型
    • 该键从结果类型中被移除
    • 编译器发出一个错误
  2. 当应用于元组 [number, string] 时,{ readonly [K in keyof T]: T[K] } 产生什么?
    • 一个带有键 0、1 和 length 的对象
    • readonly [number, string]
    • readonly (number | string)[]
    • 一个编译错误
  3. 映射类型中 -? 修饰符的作用是什么?
    • 让属性变为可选
    • 移除可选性以及它引入的 undefined
    • 移除 readonly 修饰符
    • 从类型中移除该属性