实现 RequireExactlyOne 工具类型,用于满足以下功能。即只能包含 age 或 gender 属性,不能同时包含这两个属性。具体的使用示例如下所示:
interface Person {
name: string;
age?: number;
gender?: number;
}
// 只能包含Keys中唯一的一个Key
type RequireExactlyOne<T, Keys extends keyof T> = // 你的实现代码
const p1: RequireExactlyOne<Person, 'age' | 'gender'> = {
name: "lolo",
age: 7,
};
const p2: RequireExactlyOne<Person, 'age' | 'gender'> = {
name: "lolo",
gender: 1
};
// Error
const p3: RequireExactlyOne<Person, 'age' | 'gender'> = {
name: "lolo",
age: 7,
gender: 1
};
答案
type RequireExactlyOne<
T,
K extends keyof T,
Keys extends keyof T = K
> = K extends any
? Omit<T, K> & Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, never>>
: never;
const p1: RequireExactlyOne<Person1, 'age' | 'gender'> = {
name: 'lolo',
age: 7,
};
const p2: RequireExactlyOne<Person1, 'age' | 'gender'> = {
name: 'lolo',
gender: 1,
};
// Error
const p3: RequireExactlyOne<Person1, 'age' | 'gender'> = {
name: 'lolo',
age: 7,
gender: 1,
};
我有点不理解为什么需要加上
在我的理解中,已经通过extends实现了联合类型的分发机制,那么只需要Omit<T, K> & Required<Pick<T, K>> 就可以实现题目的要求了,Partial<Record<Exclude<Keys, K>, never>>本身是可有可无的,但是去掉这个确实p3不会报错,没有实现题目要求
Partial<Record<Exclude<Keys, K>, never>>
作用是让前面已经 required 的 K 以外的其余 key 变为never
。比如当 K 为
'age'
的时候进来执行这条语句时,前半部分让'age'
变为 required 了, 后半部分从 Keys('age' | 'gender'
)中排除了'age'
(即剩下'gender'
)后让其对应的值变成了never
。借助 vscode 的错误原因分析其实你就可以知道为啥了(看你的编辑器应该不是 vscode):