如何从 TypeScript 中的合并枚举中排除某些项目?

How can I exclude some items from a merged enum in TypeScript?

我定义了以下枚举:

enum myEnum {
    a = 1,
    b = 2
}

enum mySecondEnum {
    c = 3,
    d = 4
}

后来将它们合并在一起,形成一个枚举:

const mergedEnum = {...myEnum, ...mySecondEnum};
type mergedEnum = typeof mergedEnum;

如果我现在想从合并的枚举中排除一个特定的值,我会得到一个奇怪的打字稿错误,我无法理解:

/** Type '{ [x: number]: string; c: mySecondEnum.c; d: mySecondEnum.d; a: myEnum.a; b: myEnum.b; }' does not satisfy the constraint 'string | number | symbol'.
  Type '{ [x: number]: string; c: mySecondEnum.c; d: mySecondEnum.d; a: myEnum.a; b: myEnum.b; }' is not assignable to type 'symbol'.(2344) **/
const mergedOmitedEnum: Record<Exclude<mergedEnum, 1>, number> = {
    ...
}
**/

但是,使用相同的方法,使用非合并枚举,按预期工作:

/** This does work as expected, it omits the "1" from "myEnum" */
const simpleOmitedEnum: Record<Exclude<myEnum, 1>, number> = {
    "2": 1
}

如何合并多个枚举,然后从合并的结果集中省略一些值?

这是一个playground link

考虑这一行:

const mergedEnum = { ...myEnum, ...mySecondEnum }

作为反模式。 enums 是静态已知对象,您不应该对它们进行任何操作。

你应该知道枚举的编译版本比代码表示更宽。是双向数据结构。

如果你还想合并这些数据结构,你应该改用不可变对象。

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

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


const MergedEnum = { ...MyEnum, ...MySecondEnum };
type MergedEnum = typeof MergedEnum;

此外,请记住 TS 类型系统不允许您创建动态枚举数据结构。

如果你想对枚举类型进行一些类型操作,你可以试试这个:

enum MyEnum {
    a = 1,
    b = 2
}

type Enum = `${MyEnum}`

type EnumType = Record<string | number, string | number>

type EnumToObj<Enum extends EnumType> = Pick<
    {
        [Prop in keyof Enum]:
        (Enum[Prop] extends string | number
            ? `${Enum[Prop]}`
            : never)
    }, keyof Enum
>

// type Result = {
//     readonly a: "1";
//     readonly b: "2";
// }
type Result = EnumToObj<typeof MyEnum>

Playground

如果您对双向类型感兴趣,请查看:

enum MyEnum {
    a = 1,
    b = 2
}

type Enum = `${MyEnum}`

type EnumType = Record<string | number, string | number>

type ToBidirectional<Obj extends EnumType> = {
    [Prop in keyof Obj as Obj[Prop]]: Prop
}
type EnumToObj<Enum extends EnumType> = Pick<
    {
        [Prop in keyof Enum]:
        (Enum[Prop] extends string | number
            ? `${Enum[Prop]}`
            : never)
    }, keyof Enum
>

type Bidirectional<T extends EnumType> = T & ToBidirectional<T>

type PseudoEnum = Bidirectional<EnumToObj<typeof MyEnum>>

// type Test = {
//     readonly a: "1";
//     readonly b: "2";
//     readonly 1: "a";
//     readonly 2: "b";
// }
type Test = {
    [Prop in keyof PseudoEnum]: PseudoEnum[Prop]
}

Playground

现在很容易省略键:

type CustomType=Omit<PseudoEnum, 1>

有关使用枚举类型表示的更多信息,您可以在我的 article

中找到

创建 mergedEnum 类型的最简单方法是合并两个不同的枚举

type mergedEnum = myEnum | mySecondEnum;

Playground Link

通常你会想要获取 mergedEnum 常量中值的类型,typeof mergedEnum 是常量本身的类型。要正常获取所有值的类型,我们可以做 typeof mergedEnum[keyof typeof mergedEnum]。但是因为枚举对象有从numberstring的索引签名,合并后的对象也会有这个签名。由于原始枚举是 number 枚举,我们可以删除从 typeof mergedEnum[keyof typeof mergedEnum]

获得的联合中额外的 string
type mergedEnum =  Exclude<typeof mergedEnum [keyof typeof mergedEnum ], string>;

Playground Link