组合枚举值并检查该枚举中是否有另一个值

Combining enum values and checking if another value is in that enum

考虑以下代码:

enum myEnum {
    a = 1,
    b = 2
}

enum mySecondEnum {
    c = 3,
    d = 4
}

enum myThirdEnum {
    e = 5,
    f = 6
}

/** This is an array, that should contain numbers from 1 to 4 - instead, it also contains the string keys of the enums - is there a way to limit this array to only */
const enumValuesArray = [...Object.values(myEnum), ...Object.values(mySecondEnum)];
console.log(enumValuesArray);

/** Here, b can be a number from 1 to 6 */
const b: myEnum | mySecondEnum | myThirdEnum = 5;


/** Why is typescript complaining here? I understand, that b CAN be 5 or 6 (as it is), but that is exactly the thing I want to check */
if (enumValuesArray.includes(b)) {
    console.log("Included");
} else console.log("Not included");

虽然此代码按预期编译和运行,但当我尝试检查输出数组是否包含“b”的值时,TypeScript 会报错。我收到的错误消息如下:

Argument of type 'myEnum | mySecondEnum | myThirdEnum' is not assignable to parameter of type 'string | myEnum | mySecondEnum'. Type 'myThirdEnum.e' is not assignable to type 'string | myEnum | mySecondEnum'.(2345)

我该如何解决这个错误?

作为第二个注意事项,是否有可能以某种方式仅检索枚举值数组,而不是枚举值和属性?

首先,值得使用不可变对象而不是数字枚举。 参见示例:

const myEnum = {
    a: 1,
    b: 2
} as const

const mySecondEnum = {
    c: 3,
    d: 4
} as const

const myThirdEnum = {
    e: 5,
    f: 6
} as const

现在,enumValuesArray 有一个预期的类型:

// (1 | 2 | 3 | 4)[]
const enumValuesArray = [...Object.values(myEnum), ...Object.values(mySecondEnum)];

至于Array.prototype.includes的错误。这是已知问题。 TS 只允许您使用 enumValuesArray.

中存在的数字

有一个通用但有点冗长的解决方法。 优点:不需要类型断言 缺点:冗长且需要你咖喱。

完整示例:

const myEnum = {
    a: 1,
    b: 2
} as const

const mySecondEnum = {
    c: 3,
    d: 4
} as const

const myThirdEnum = {
    e: 5,
    f: 6
} as const

type Values<T> = T[keyof T]

type Primitives =
    | string
    | number
    | bigint
    | boolean
    | symbol
    | null
    | undefined

type InferPrimitive<T, P> = P extends any ? T extends P ? P : never : never;

type Inference<T> = InferPrimitive<T, Primitives>


const withTuple = <
    List extends Primitives[]
>(list: readonly [...List]) =>
    (prop: Inference<List[number]>):
        prop is Inference<List[number]> & List[number] =>
        list.includes(prop)


// (1 | 2 | 3 | 4)[]
const enumValuesArray = [...Object.values(myEnum), ...Object.values(mySecondEnum)];

const includes = withTuple(enumValuesArray);

type DistributeValues<T> = T extends any ? Values<T> : never

// 1 | 2 | 3 | 4 | 5 | 6
type AllowedValues = DistributeValues<typeof myEnum | typeof mySecondEnum | typeof myThirdEnum>


/** Here, b can be a number from 1 to 6 */
const b: AllowedValues = 5; // ok


/** Why is typescript complaining here? I understand, that b CAN be 5 or 6 (as it is), but that is exactly the thing I want to check */
if (includes(b)) {
    console.log("Included");
} else console.log("Not included");

includes('a') // expected error

Playground

有关 includes 的更多说明,您可以在我的 article

中找到

InferPrimitive - 将文字类型缩小为更宽的类型。例如 InferPrimitive<42> 将 return number.