TypeScript 映射类型
映射类型
有的时候一个类型建立在另一个类型的基础之上,这时候可以考虑使用映射类型。 映射类型建立在索引签名类型的基础上,让我们先回顾下索引类型签名:
type OnlyStrAndNum = {
[key: string]: string | number;
};
const conforms: OnlyStrAndNum = {
name: 'zsd',
age: 19,
}
而映射类型,就是使用了 PropertyKeys
联合类型的泛型,其中 PropertyKeys
多是通过 keyof
创建,然后循环遍历键名创建一个类型:
type OptionsFlags<T> = {
[Property in keyof T]: boolean;
}
在这个例子中OptionsFlags
会遍历type
的所有属性,然后设置为布尔类型:
type FeatureFlags = {
darkMode: () => void;
newUserProfile: () => void;
}
type FeatureOptions = OptionsFlags<FeatureFlags>;
// type FeatureOptions = {
// darkMode: boolean;
// newUserProfile: boolean;
// }
映射修饰符
在使用映射类型时,有两个额外的修饰符可能是会用到的,一个是readonly
设置属性为只读,另一个就是可选修饰符?
,你可以通过写前缀加号+
减号-
来添加或者删除这些修饰符,如果没有写前缀,相当于使用了+
前缀。
// 删除属性中的只读属性
type CreateMutable<T> = {
- readonly [Property in keyof T]: T[Property];
}
type LockedAccount = {
readonly id: string;
readonly name: string;
}
type UnlockedAccount = CreateMutable<LockedAccount>;
// type UnlockedAccount = {
// id: string;
// name: string;
// }
// 删除属性中的可选属性
type Concrete<T> = {
[Property in keyof T] -?: T[Property];
}
type MaybeUser = {
id: string;
name?: string;
age?: number;
}
type User = Concrete<MaybeUser>;
// type User = {
// id: string;
// name: string;
// age: number;
// }
通过as
实现键名重新映射
在TypeScript4.1以后,你可以在映射类型中使用as
语句对键名重新映射:
type MappedTypeWithNewProperties<T> = {
[Property in keyof T as NewPropertyKey]: T[Property];
}
举个例子,你可以使用模板字面量类型,基于之前的属性名创建一个新属性名:
type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
interface Person {
id: number;
name: string;
age: number;
sex: string;
}
type LazyPerson = Getters<Person>;
// type LazyPerson = {
// getId: () => number;
// getName: () => string;
// getAge: () => number;
// getSex: () => string;
// }
你也可以使用Exclude
过滤掉某一些属性:
type RemoveKindField<T> = {
[Property in keyof T as Exclude<Property, 'kind'>]: T[Property]
};
interface Circle {
kind: 'circle',
radius: number;
}
type KindLessCircle = RemoveKindField<Circle>;
你可以遍历任何联合类型,不仅仅是string|number|symbol
这种联合类型,可以是任意联合类型:
type EventConfig<Events extends { kind: string }> = {
// 这里E是类型,使用索引访问类型取到kind
[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;
// }
深入探索
映射类型也可以搭配其他的功能使用,例如我们有一个使用条件类型的映射类型,会根据对象是否有pii
属性返回true
或者false
:
type ExtractPII<T> = {
[Property in keyof T]: T[Property] extends { pii: boolean } ? true : false
}
type DBFields = {
id: { format: 'incrementing' };
name: { type: 'string'; pii: true };
}
type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>
// type ObjectsNeedingGDPRDeletion = {
// id: false;
// name: true;
// }