ts提供了映射类型,用于重用和修改已存在的类型。
假如我们有一个Person
接口,我们希望把Person
内部所有的成员都变成可选。在不借助映射类型时,我们可以会这样做:
interface Person {
name: string;
age: number;
}
interface OptionalPerson {
name?: string;
age?: number;
}
这样既繁琐,也没有做到类型复用。而借助映射类型:
type OptionalPerson = { [K in keyof Person]?: Person[K] };
映射类型使用形如[K in KEYS]
的形式遍历类型中所有属性名,此时的KEYS
是通过keyof Person
返回所有属性名所组成的联合类型。并通过添加?
操作符将所有属性变为可选类型。
如果我们希望把Person
所有属性变成只读的:
type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] };
+/-操作符
如果Person
中所有属性就是只读类型,那么我们有没有办法去掉只读限制呢?我们可以使用+
或者-
操作符表示添加或者删除某种限制。
interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}
type Person = { -readonly [K in keyof ReadonlyPerson]: Person[K] };
如果Person
中所有属性就是可选类型,将所有可选限制去掉:
interface OptionalPerson {
name?: string;
age?: number;
}
type Person = { [K in keyof OptionalPerson]-?: Person[K] };
readonly
等价于:+readonly
;?
等价于:+?
。
深层映射
如果对象结构有很多层。使用上述的例子只能遍历和修改第一层属性;如果深层映射,我们可以递归调用映射类型。如希望一个类型所有层级的属性都为可选:
interface DeepObj {
name: string;
age: number;
deepAttributes: {
prop1: string;
prop2: string;
obj: {
other: number;
};
};
}
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
type PartialDeepOjb = DeepPartial<DeepObj>;
在DeepPartial
中,我们会使用条件类型判断当前的属性类型是否是object
,如果是则递归映射,否则直接使用当前类型。