好的,所以我们可以做到,但它不是微不足道的。最简单的就是找出我们的类型是否至少有一个键。
type AtLeastOneProp<X, Pattern> = (keyof X & keyof Pattern) extends never ? never : X
// use
type B = AtLeastOneProp<{x: string}, {x: string, y: string}> // ? correct has at least one prop - x
type C = AtLeastOneProp<{w: string}, {x: string, y: string}> // ? never as has no props
type D = AtLeastOneProp<{x: string, y: number, z: number}, {x: string, y: string}> // ? ok has more than one prop
我们在这里所做的是检查键的交集是否给出任何结果。如果是,我们返回原始类型。
更难的是强制我们的类型中只能存在一个键。
type IsSingleVariantOrMore<U extends PropertyKey, Result extends 0 | 1 | 'more' = 0, NextResult extends 0 | 1 | 'more' = Result extends 0 ? 1 : Result extends 1 ? 'more' : 'more'> =
({
[K in U]: Exclude<U, K> extends never ? NextResult : IsSingleVariantOrMore<Exclude<U, K>, NextResult>
})[U]
type HasOnlyOneProp<X, Pattern> = IsSingleVariantOrMore<keyof X & keyof Pattern> extends 1 ? X : never;
// use
type E = HasOnlyOneProp<{x: string, z: number}, {x: string, y: string}>; // ? correct has only x
type F = HasOnlyOneProp<{x: string, y: number}, {x: string, y: string}>; // ? wrong it has two props D is never
我在这里所做的是使用IsSingleVariantOrMore 来计算我们是否有包含一个或多个可能元素的变体。函数HasOnlyOneProp 只是检查我们是否有1,如果有,则意味着类型只有一个共同的道具,任何其他结果意味着该类型没有相交的道具或有多个。
完整代码在the playground。