业务中常常有互斥属性的需求存在,如A,B两个属性,A与B互斥,通常我们会用可选类型去完成
type AB = {
A?: number;
B?: number;
}
const ab:AB = {
A: 1,
B: 2,
}
这么做会有个问题,那就是a和b有可能同时存在,也可能同时不存在
那换成联合类型呢
type A = {
A: 1;
}
type B = {
B: 1;
}
const a:A = {
A: 1,
}
const b:B = {
B: 1,
}
这样子确实可以完成需求了,可是要写定义两次类型不够优雅(不能偷懒),有没有什么办法可以剔除掉指定的字段再和原来的类型合并,这样子不就可以实现了吗。
首先我们要实现一个剔除指定字段的类型
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
Exclude是TS的高级类型,如果 T 是 U 的子类型则返回 never 不是则返回 T
然后就可以和原来的类型合并,原来的类型需要剔除字段,可以用Omit类型完成
type XOR<T, A extends keyof T, B extends keyof T> = (Without<Omit<T, A>, Omit<T, B>> & Omit<T, B>) | (Without<Omit<T, B>, Omit<T, A>> & Omit<T, A>);
type ABC = {
A: number,
B: number,
C: number,
}
//------------
//A和B互斥
const abc1: XOR<ABC, 'A', 'B'> = {
A: 1,
B: 2,
C: 3,
}
//Types of property 'A' are incompatible. Type 'number' is not assignable to type 'undefined'.
//------------
//正确写法
const abc2: XOR<ABC, 'A', 'B'> = {
A: 1,
C: 1,
}
const abc3: XOR<ABC, 'A', 'B'> = {
B: 2,
C: 1,
}
//其他属性不受影响,C必填,没有填会触发检验
const abc4: XOR<ABC, 'A', 'B'> = {
A: 2,
}
// Property 'C' is missing in type '{ A: number; }' but required in type 'Omit<ABC, "B">'.