如何定义至少包含一个true值的布尔对象类型
如何定义至少包含一个true值的布尔对象类型
嘿,我完全懂你想实现的需求——要确保AgeDivisions和EventStyles这两个类型的实例,至少有一个属性的值是true对吧?其实用泛型来做是最优雅的方案,既能避免重复代码,又能让所有类似的布尔对象类型都复用同一个约束逻辑。
核心思路:泛型约束类型
我们可以创建一个通用的泛型类型AtLeastOneTrue<T>,它接受一个布尔属性的对象类型T,然后约束这个对象必须至少有一个属性为true。具体实现如下:
// 定义泛型类型:确保传入的布尔对象至少有一个true值 type AtLeastOneTrue<T extends Record<string, boolean>> = { // 保留原类型的所有属性结构 [K in keyof T]: T[K] } & { // 生成一个联合类型:每个成员对应某一个属性为true,其余属性可选为false [K in keyof T]: { [P in K]: true } & { [P in Exclude<keyof T, K>]?: false } }[keyof T];
这个类型的逻辑是:
- 第一部分
{ [K in keyof T]: T[K] }保留原类型的完整结构; - 第二部分生成一个联合类型,每个联合成员对应原类型中某一个属性强制为true,其他属性则是可选的false;
- 通过交叉类型
&把两部分结合,最终就确保了对象至少有一个属性是true。
应用到你的类型上
接下来把这个泛型应用到你现有的AgeDivisions和EventStyles上,再更新EventInfo类型即可:
type AgeDivisions = { youth: boolean; middleSchool: boolean; highSchool: boolean; college: boolean; open: boolean; }; type EventStyles = { folkstyle: boolean; freestyle: boolean; grecoRoman: boolean; jello: boolean; noGiJits: boolean; giJits: boolean; judo: boolean; beach: boolean; }; // 生成符合约束的有效类型 type ValidAgeDivisions = AtLeastOneTrue<AgeDivisions>; type ValidEventStyles = AtLeastOneTrue<EventStyles>; // 假设你已经定义了EventType类型 type EventType = "tournament" | "workshop" | "exhibition"; type EventInfo = { name: string; venueName: string; address: string; date: string; subTitle: string; country: string; eventType: EventType; hasGirlsOnlyDivision: boolean; link: string; ageDivisions: ValidAgeDivisions; // 替换成约束后的类型 eventStyles: ValidEventStyles; // 替换成约束后的类型 latitude: string; longitude: string; description: string; };
验证效果
现在如果尝试创建一个所有属性都是false的ValidAgeDivisions对象,TypeScript会直接报错:
// ❌ 错误:所有属性都是false,不符合约束 const invalidAgeDivisions: ValidAgeDivisions = { youth: false, middleSchool: false, highSchool: false, college: false, open: false }; // ✅ 正确:至少有一个属性是true const validAgeDivisions: ValidAgeDivisions = { youth: true, middleSchool: false, highSchool: false, college: false, open: false };
备选方案:单独定义约束(不推荐)
如果你觉得泛型有点绕,也可以为每个类型单独写约束,但这种方式会产生大量重复代码,维护成本很高,比如:
type ValidAgeDivisions = | { youth: true } & Partial<Record<Exclude<keyof AgeDivisions, 'youth'>, false>> | { middleSchool: true } & Partial<Record<Exclude<keyof AgeDivisions, 'middleSchool'>, false>> | { highSchool: true } & Partial<Record<Exclude<keyof AgeDivisions, 'highSchool'>, false>> | { college: true } & Partial<Record<Exclude<keyof AgeDivisions, 'college'>, false>> | { open: true } & Partial<Record<Exclude<keyof AgeDivisions, 'open'>, false>>;
显然这种方式不如泛型简洁,所以还是更推荐用泛型方案。
备注:内容来源于stack exchange,提问作者Wayne
相关产品推荐
相关产品推荐

