挑战
实现一个通用的PartialByKeys<T, K>,它接收两个类型参数T和K。
K指定应设置为可选的T的属性集。当没有提供K时,它就和普通的Partial<T>一样使所有属性都是可选的。
例如:
interface User {
name: string;
age: number;
address: string;
}
type UserPartialName = PartialByKeys<User, 'name'>; // { name?:string; age:number; address:string }解答
这道题我们可以根据 K 把属性分成两部分,一部分是可选的,一部分是必选的。然后再把这两部分合并起来。
type PartialByKeys<T, K> = {
[P in keyof T as P extends K ? P : never]?: T[P];
} & {
[P in keyof T as P extends K ? never : P]: T[P];
};但是题目要求我们返回的是一整个对象,所以我们需要把这两部分合并起来。
我们创建一个辅助类型 IntersectionToObj 来合并这两部分。
type IntersectionToObj<T> = {
[P in keyof T]: T[P];
};
type PartialByKeys<T, K> = IntersectionToObj<
{
[P in keyof T as P extends K ? P : never]?: T[P];
} & {
[P in keyof T as P extends K ? never : P]: T[P];
}
>;再完善一下,当 K 没有传入时,我们需要给 K 一个默认值。
type PartialByKeys<T, K extends keyof T = keyof T> = IntersectionToObj<
{
[P in keyof T as P extends K ? P : never]?: T[P];
} & {
[P in keyof T as P extends K ? never : P]: T[P];
}
>;最后,我们也可以使用内置的 Exclude 来简化这个实现。
type PartialByKeys<T, K extends keyof T = keyof T> = IntersectionToObj<
{
[P in keyof T as P extends K ? P : never]?: T[P];
} & {
[P in Exclude<keyof T, K>]: T[P];
}
>;
type IntersectionToObj<T> = {
[P in keyof T]: T[P];
};