如何用TypeScript泛型简洁实现互斥属性组合约束?
问题:用泛型简化互斥属性组合的TypeScript类型定义
我需要定义一个TypeScript类型,要求用户只能传入以下两种属性组合之一:
data+color+hasColordata+info+hasInfo
任何其他组合都不允许。
原可行但繁琐的实现
这种写法能满足需求,但代码过于啰嗦:
type Props = { data: string; } & ( | { info?: string; hasInfo?: boolean; color?: never; hasColor?: never; } | { info?: never; hasInfo?: never; color?: string; hasColor?: boolean; } ); function foo(props: Props) { console.log("bar"); } foo({ data: "hello", info: "hello", hasInfo: true }); // TypeScript 无报错 foo({ data: "hello", info: "hello", hasColor: true }); // TypeScript 报错
我想知道有没有用泛型实现的更简洁方式?
我尝试的泛型写法(存在问题)
下面的写法逻辑有问题,导致合法调用也报错:
type Info = { info: string; hasInfo: boolean }; type Color = { color: string; hasColor: boolean }; type Data = { data: string }; function foo<T>( props: keyof T extends keyof Info ? Data & Info : Data & Color ) { console.log("bar"); } foo({ data: "hello", color: "hello", hasColor: true }); // TypeScript 报错
泛型简化方案
可以通过泛型约束+交叉类型实现更简洁的定义,核心是让泛型仅约束在两组互斥属性范围内,再和公共属性Data交叉:
允许仅传data的版本(属性可选)
type Info = { info?: string; hasInfo?: boolean }; type Color = { color?: string; hasColor?: boolean }; type Data = { data: string }; // 约束泛型T只能是Info或Color的子类型 function foo<T extends Info | Color>(props: Data & T) { console.log("bar"); } // 合法调用 foo({ data: "hello", info: "hello", hasInfo: true }); foo({ data: "hello", color: "hello", hasColor: true }); foo({ data: "hello" }); // 仅传data也允许 // 非法调用(同时包含两组属性) foo({ data: "hello", info: "hello", hasColor: true }); // TypeScript 报错
必须传入对应组属性的版本(属性必填)
如果要求不能只传data,必须搭配某一组的属性,可将Info和Color的属性设为必填:
type Info = { info: string; hasInfo: boolean }; type Color = { color: string; hasColor: boolean }; type Data = { data: string }; function foo<T extends Info | Color>(props: Data & T) { console.log("bar"); } // 合法调用 foo({ data: "hello", info: "hello", hasInfo: true }); foo({ data: "hello", color: "hello", hasColor: true }); // 非法调用 foo({ data: "hello" }); // 报错:缺少Info或Color的属性 foo({ data: "hello", info: "hello", hasColor: true }); // 报错:同时包含两组属性
原泛型写法的问题原因
你之前的写法中,keyof T extends keyof Info的逻辑存在缺陷:当传入包含color的对象时,泛型T会被推断为Data & Color,此时keyof T包含"data",而"data"不属于keyof Info,导致条件判断错误,最终返回的类型和传入的属性不匹配,引发报错。
而新方案直接约束T为Info或Color的子类型,再和Data交叉,避免了data属性干扰类型判断的问题。
内容的提问来源于stack exchange,提问作者Tasos Tsournos
相关产品推荐
相关产品推荐

