如何检查对象中的所有字段是否都为“假”而只有一个为“真”?

问题描述

我有一个包含许多布尔字段的对象,如下所示:

type SomeObject = {
  A: boolean
  B: boolean
  C: boolean
  ....
}

是否有一种简单/有效的方法来检查所有其他字段(指定字段除外)的 OR 是否为 false

我们可以通过手动检查每个字段来暴力破解:

let foo:SomeObject = {
  A: false
  B: false
  C: false
}
let isA = !(foo.B || foo.C)
let isB = !(foo.A || foo.C)
let isC = !(foo.A || foo.B)

但我相信有一种更优雅的方式来实现这一点。

解决方法

如果您只需要编译时检查,使用映射类型和交集(为了“美化”生成的类型而抛出的身份映射类型)很容易做到:

type SomeObject = {
  A: boolean
  B: boolean
  C: boolean
}

type OnlyOneTrue<T extends object,K extends keyof T> = {
    [P in K] : true
} & {
    [P in Exclude<keyof T,K>]: false
};

type Identity<T> = { [P in keyof T] : T[P] }

//{ A: true; } & { B: false; C: false; }
type test = OnlyOneTrue<SomeObject,"A">;

//{ A: true; B: false; C: false; }
type pretty = Identity<test>;

如果您需要编译时类型保护以便稍后可以缩小类型,那么您需要使用联合。前一种类型可以成为另一种实用类型的垫脚石:

type PossibleOnlyTrue<T extends object> = {
    [P in keyof T]: OnlyOneTrue<T,P>
}[keyof T];

//OnlyOneTrue<SomeObject,"A"> | OnlyOneTrue<SomeObject,"B"> | OnlyOneTrue<SomeObject,"C">
type union = PossibleOnlyTrue<SomeObject>;

const process = (obj: union) => {
    //type is narrowed to a union member:
    if(obj.A) {
        console.log(obj); //OnlyOneTrue<SomeObject,"A">
    }
};

如果您需要在顶部进行运行时检查,有一些选项。下面是基于之前的辅助映射类型(PossibleOnlyTrueOnlyOneTrue)的类型和运行时保护:

const runtime = <K extends keyof union>(obj: union,key: K) : obj is Extract<union,{ [P in K] : true }> => {
    return Object.entries(obj).every(([k,v]) => k === key || !v );
};

{
    const obj:union = { A:true,B:false,C:false };
    if(runtime(obj,"A")) obj //OK,obj is OnlyOneTrue<SomeObject,"A">
}

Playground

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...