文章目录
TypeScript 是一种强类型的 JavaScript 超集,允许开发者在构建大型应用时获得更好的类型安全。本文将详细介绍 TypeScript 中的映射类型及其应用,通过映射类型,开发者可以基于现有类型创建新的类型,从而避免重复代码,提高可维护性。
一、映射类型概述
1. 什么是映射类型
映射类型是一种基于现有类型生成新类型的机制。它的语法基于索引签名,允许开发者动态地为类型的属性定义新的类型。通过映射类型,可以轻松地将一种类型的属性转换为另一种类型的属性,避免手动逐个定义。
2. 映射类型的基本语法
映射类型的基本语法如下:
type MappedType<OriginalType> = {
[Property in keyof OriginalType]: NewType;
};
在这个语法中,OriginalType
是要映射的原始类型,Property
是其属性,NewType
是新属性的类型。下面是一个简单的例子:
type OnlyBoolsAndHorses = {
[key: string]: boolean | Horse;
};
在这个例子中,OnlyBoolsAndHorses
是一个映射类型,表示所有字符串键的值可以是布尔值或 Horse
类型。
二、映射类型的应用实例
1. 基于已有类型创建新类型
通过映射类型,我们可以轻松地将已有类型的属性转换为其他类型。以下示例展示了如何将一个对象类型的所有属性转换为布尔值:
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
type Features = {
darkMode: () => void;
newUserProfile: () => void;
};
type FeatureOptions = OptionsFlags<Features>;
// 结果:
// type FeatureOptions = {
// darkMode: boolean;
// newUserProfile: boolean;
// }
在这个例子中,OptionsFlags
将 Features
类型的所有属性的类型更改为布尔值。
2. 修改映射属性的可变性和可选性
TypeScript 中的映射类型还允许我们使用修饰符来控制属性的可变性和可选性。常用的修饰符包括 readonly
和 ?
,可以通过前缀 -
或 +
来移除或添加这些修饰符。
例子:移除 readonly
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};
type LockedAccount = {
readonly id: string;
readonly name: string;
};
type UnlockedAccount = CreateMutable<LockedAccount>;
// 结果:
// type UnlockedAccount = {
// id: string;
// name: string;
// }
例子:移除可选属性
type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property];
};
type MaybeUser = {
id: string;
name?: string;
age?: number;
};
type User = Concrete<MaybeUser>;
// 结果:
// type User = {
// id: string;
// name: string;
// age: number;
// }
三、键重映射
TypeScript 4.1 及以上版本允许通过 as
子句在映射类型中进行键重映射,这使得我们可以根据需要重新定义属性名称。
使用 as
进行键重映射
type Getters<Type> = {
[Property in keyof Type as `get${
Capitalize<string & Property>}`]: () => Type[Property];
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
// 结果:
// type LazyPerson = {
// getName: () => string;
// getAge: () => number;
// getLocation: () => string;
// }
在这个例子中,我们通过模板字面量类型为 Person
的属性生成了新的 getter 函数。
四、过滤属性
映射类型还可以结合条件类型来过滤掉某些属性,创造出新的类型。
移除特定属性
以下示例展示了如何移除对象中的某个属性:
type RemoveKindField<Type> = {
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property];
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
// 结果:
// type KindlessCircle = {
// radius: number;
// }
在这个例子中,我们使用 Exclude
来过滤掉 kind
属性。
五、对任意联合类型的映射
映射类型不仅限于字符串、数字或符号的联合类型,还可以对任何类型的联合进行映射。
映射任意事件配置
type EventConfig<Events extends {
kind: string }> = {
[E in Events as E["kind"]]: (event: E) => void;
};
type SquareEvent = {
kind: "square"; x: number; y: number };
type CircleEvent = {
kind: "circle"; radius: number };
type Config = EventConfig<SquareEvent | CircleEvent>;
// 结果:
// type Config = {
// square: (event: SquareEvent) => void;
// circle: (event: CircleEvent) => void;
// }
在这个例子中,EventConfig
将不同类型的事件映射为函数。
六、进一步探索映射类型
映射类型与 TypeScript 的其他特性结合良好。以下示例展示了一个使用条件类型的映射类型,根据对象的某个属性生成新的布尔值:
提取 PII(个人身份信息)
type ExtractPII<Type> = {
[Property in keyof Type]: Type[Property] extends {
pii: true } ? true : false;
};
type DBFields = {
id: {
format: "incrementing" };
name: {
type: string; pii: true };
};
type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;
// 结果:
// type ObjectsNeedingGDPRDeletion = {
// id: false;
// name: true;
// }
在这个例子中,我们提取了标记为 PII 的字段,返回一个布尔值表示是否需要删除。
七、总结
TypeScript 中的映射类型提供了一种强大且灵活的方式来创建新类型。通过映射类型,开发者可以高效地基于现有类型构建新的类型,避免重复代码,提升代码的可维护性。希望本文能帮助你更好地理解映射类型的用法,并在实际项目中充分发挥其潜力,从而提升开发效率和代码质量。
推荐: