【问题标题】:How to ensure a Typescript string enum has the same key and value如何确保 Typescript 字符串枚举具有相同的键和值
【发布时间】:2019-12-03 19:27:38
【问题描述】:

我想创建一个通用类型来检查枚举上的以下内容:

  1. 所有字段都是字符串
  2. 所有的值都等于它们自己的键

那么在这种情况下,以下枚举将被认为是“正确的”:

enum correct1 {
  bar = 'bar',
  baz = 'baz',
}

enum correct2 {
  quux = 'quux',
}

但以下不会:

enum wrongFoo {
  bar = 'bar',
  baz = 'WRONG',
}

enum wrongFoo2 {
  bar = 1
}

实现这一点的正确语法是什么?

【问题讨论】:

    标签: typescript enums


    【解决方案1】:

    避免每次检查都必须声明新类型的替代方法:

    const keyValuesMatch = <T>(kv: { [K in keyof T]: K }) => {};
    
    enum correct {
      bar = 'bar',
      baz = 'baz',
    }
    enum incorrect {
      bar = 'bar',
      baz = 'wrong',
    }
    
    keyValuesMatch(correct);
    keyValuesMatch(incorrect); // Type 'incorrect.baz' is not assignable to type '"baz"'.
    

    【讨论】:

      【解决方案2】:

      如果您可以接受手动编译时检查(这意味着您必须在 enum 定义之后手动编写一些内容),您可以这样做:

      type EnsureCorrectEnum<T extends { [K in Exclude<keyof T, number>]: K }> = true;
      

      然后让编译器评估EnsureCorrectEnum&lt;typeof YourEnumObjectHere&gt;。如果它编译,很好。如果没有,那就有问题了:

      type Correct1Okay = EnsureCorrectEnum<typeof correct1>; // okay
      type Correct2Okay = EnsureCorrectEnum<typeof correct2>; // okay
      
      type WrongFooBad = EnsureCorrectEnum<typeof wrongFoo>; // error!
      //   ┌─────────────────────────────> ~~~~~~~~~~~~~~~
      // Types of property 'baz' are incompatible.
      
      type WrongFoo2Bad = EnsureCorrectEnum<typeof wrongFoo2>; // error!
      //   ┌──────────────────────────────> ~~~~~~~~~~~~~~~~
      // Types of property 'bar' are incompatible.
      

      这些错误也相当具有描述性。

      好的,希望对您有所帮助;祝你好运!

      Link to code

      【讨论】:

      • 你能解释一下你的模板是如何工作的吗?
      • T extends { [K in Exclude&lt;keyof T, number&gt;]: K } 约束意味着“T 必须可分配给所有非数字键 (K in Exclude&lt;keyof T, number&gt;) 的值与键 (K) 相同的对象类型)”。这有意义吗?
      • 绝对!我不确定排除是什么意思,但我猜纯字符串枚举不需要兽人
      【解决方案3】:

      听起来您可以使用我们编写的实用程序。它本身并没有创建枚举,但它确实创建了一个类型安全的对象,其中键以它们的名称作为它们的值,并使用 keyof typeof 来获得一些字符串类型的安全性。

      这是在字符串枚举存在之前创建的,这就是为什么称为枚举,但实际上并不是枚举。它只是一个对象,但您可以使用它来代替硬编码字符串。

      /**
       * This creates a fake string enum like object.  Use like so:
       *     const Ab = strEnum(['a', 'b']);
       *     type AbKeys = keyof typeof Ab;
       * @param keys keys in the enum
       * @returns enum object
       */
      export function createStringEnum<T extends string>(keys: T[]): {[K in T]: K} {
          return keys.reduce((res, key) => {
              res[key] = key;
              return res;
          }, Object.create(null));
      }
      
      const Ab = createStringEnum(['a', 'b']);
      type AbKeys = keyof typeof Ab;
      
      const Bc = createStringEnum(['b', 'c']);
      type BcKeys = keyof typeof Ab;
      
      console.log(Bc.blah) // Compilation error blah property does not exist
      // Error invalid string
      const b: AbKeys = "e"; 
      // An enum throws an error, but this isn't really an enum
      // Main drawback of this approach
      const a: AbKeys = Bc.b;
      

      即使它不符合您的需求,这也可能对不需要使用枚举的其他人有所帮助。

      【讨论】:

        【解决方案4】:

        Typescript 中的枚举是对象,因此您可以使用 Object.keys 函数获取该枚举中的所有键并检查它们是否等于它们的值。由于 Object.keys 函数返回的所有键都是字符串,因此值也必须是字符串。

        enum correct1 {
            bar = 'bar',
            baz = 'baz',
        }
        
        enum correct2 {
            quux = 'quux',
        }
        
        enum wrongFoo {
            bar = 'bar',
            baz = 'WRONG',
        }
        
        enum wrongFoo2 {
            bar = 1
        }
        
        function isEnumValid<T extends {}>(validatedEnum: T) : boolean {
            return Object.keys(validatedEnum).every(k => k === validatedEnum[k]);
        }
        
        console.log(isEnumValid(correct1)); // true
        console.log(isEnumValid(correct2)); // true
        console.log(isEnumValid(wrongFoo)); // false
        console.log(isEnumValid(wrongFoo2)); // false
        

        【讨论】:

        • OP 是否要求运行时检查?
        • 是的,如果可能的话,最好进行编译时检查:)
        • 这不能在编译时完成。见this答案...
        • @MatějPokorný 这意味着您需要在编译时使用typeof correct1 来引用枚举对象的类型,而不仅仅是correct1
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-30
        • 2022-08-03
        相关资源
        最近更新 更多