【问题标题】:Combining enums in TypeScript在 TypeScript 中组合枚举
【发布时间】:2020-06-17 08:46:01
【问题描述】:

在 TypeScript 中组合多个枚举的好方法是什么?我的第一个直觉会告诉我这样做,但这会产生代码重复,容易出错。

export enum Formats {
  Shirt = 'shirt',
  Fruit = 'fruit',
}

export enum Shirt {
  Yellow = 'yellow',
  Orange = 'orange',
}

export enum Fruit {
  Orange = 'orange',
  Lemon = 'lemon',
}

export enum Item {
    ShirtYellow = 'shirt:yellow',
    ShirtOrange = 'shirt:orange',
    FruitOrange = 'fruit:orange',
    FruitLemon = 'fruit:lemon',
}

用例示例。枚举用于描述四个不同的对话窗口。衬衫对话处理程序定义了黄色和橙色两个对话窗口。黄色衬衫对话框和橙色衬衫对话框差异很大,以至于无法为它们使用相同类型的对话框。衬衫对话处理程序不理解水果对话。水果处理程序相似但相反。还有一个全局对话框管理器负责确保在任何给定时间只打开一个对话框。全局窗口管理器包含一个表示打开对话框的变量。此变量存储在磁盘上,以在应用/页面重新加载时保留打开的对话框状态。

【问题讨论】:

  • 你能解释一下你想用这个实现什么吗?
  • 为什么所有组合都需要枚举常量?您不能只定义一个每个枚举具有一个属性的类,如果您需要列出所有组合,您可以通过在循环中创建这些实例来简单地做到这一点?
  • @BenjaminMaurer 像这样组合它们使 TypeScript 相信这些组合是常规字符串,因为没有用于连接两个字符串文字的类型注释。见github.com/microsoft/TypeScript/issues/12940
  • 但是这样的字符串 ('shirt:yellow') 在哪里会有帮助?
  • @cyberixae 我根本不是在谈论连接字符串。我说的是用枚举属性创建 objects 来表示组合,而不是创建组合枚举(或字符串,或任何其他原始类型): class Product { format: Format;颜色:颜色; }

标签: typescript enums code-duplication


【解决方案1】:

我认为我们不应该关注像枚举这样的原始值类型。适当的记录或课程可以做你想做的事。 TypeScript 允许您构建"discriminated unions",即可以通过一个字段(“标签”)区分的一系列类型:

export enum ShirtOptions {
  Yellow = 'yellow',
  Orange = 'orange',
}

export enum FruitOptions {
  Orange = 'orange',
  Lemon = 'lemon',
}

interface Shirt {
    kind: 'shirt';
    options: ShirtOptions;
}

interface Fruit {
    kind: 'fruit';
    options: FruitOptions; // Can have a different name
}

type Format = Shirt | Fruit;

function handler(f: Format) {
    switch (f.kind) {
        case "shirt": return doShirtStuff();
        case "fruit": return doFruitStuff();
    }
}

TypeScript 会对 switch 语句进行详尽检查,并会告诉您,如果您没有处理所有情况(请参阅链接了解详细信息)。

【讨论】:

    【解决方案2】:

    在玩了一会儿之后,我想出了以下内容。仍然存在重复,但至少有一些交叉检查可能有助于防止错误。这样做的主要问题是它非常冗长,而且很容易忘记一种组合。

    type Pair<A, B> = [A, B]
    const pair = <A, B>(a: A, b: B): Pair<A, B> => [a, b]
    
    type ShirtYellow = Pair<Formats.Shirt, Shirt.Yellow>
    type ShirtOrange = Pair<Formats.Shirt, Shirt.Orange>
    type FruitOrange = Pair<Formats.Fruit, Fruit.Orange>
    type FruitLemon = Pair<Formats.Fruit, Fruit.Lemon>
    
    const ShirtYellow: ShirtYellow = pair(Formats.Shirt, Shirt.Yellow)
    const ShirtOrange: ShirtOrange = pair(Formats.Shirt, Shirt.Orange)
    const FruitOrange: FruitOrange = pair(Formats.Fruit, Fruit.Orange)
    const FruitLemon: FruitLemon = pair(Formats.Fruit, Fruit.Lemon)
    
    export type Item = ShirtYellow | ShirtOrange | FruitOrange | FruitLemon 
    export const Item = { ShirtYellow, ShirtOrange, FruitOrange, FruitLemon };
    

    这是我的第二次尝试。这次是基于对象的解决方案。

    type AbstractItem<I extends { kind: Formats, type: any }> = I
    
    export type ShirtItem = AbstractItem<{kind: Formats.Shirt, type: Shirt}>
    export type FruitItem = AbstractItem<{kind: Formats.Fruit, type: Fruit}>
    
    export type Item = AbstractItem<ShirtItem | FruitItem>
    
    export const isShirt = (i: Item): i is ShirtItem => i.kind === Formats.Shirt
    export const isFruit = (i: Item): i is FruitItem => i.kind === Formats.Fruit
    
    export const getShirt = (i: Item): Shirt|null => isShirt(i) ? i.type : null
    export const getFruit = (i: Item): Fruit|null => isFruit(i) ? i.type : null
    

    【讨论】:

      猜你喜欢
      • 2020-07-18
      • 2020-08-10
      • 1970-01-01
      • 2012-03-22
      • 2018-07-06
      • 1970-01-01
      • 2018-02-14
      • 2017-08-23
      • 2014-10-04
      相关资源
      最近更新 更多