如何确保Typescript字符串枚举具有相同的键和值

时间:2019-07-25 13:38:18

标签: typescript enums

我想创建一个泛型类型,以检查枚举中的以下内容:

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

因此,在这种情况下,以下枚举将被视为“正确”:

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

enum correct2 {
  quux = 'quux',
}

但以下内容不会:

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

enum wrongFoo2 {
  bar = 1
}

实现此目标的正确语法是什么?

4 个答案:

答案 0 :(得分:1)

听起来像您可以使用我们编写的实用程序。它本身并不会创建枚举,但是会创建一个类型安全的对象,其中键的名称为其值,并使用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;

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

答案 1 :(得分:1)

如果您可以接受手动编译时检查(意味着必须在enum定义之后手动编写内容),则可以执行以下操作:

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

然后让编译器评估EnsureCorrectEnum<typeof YourEnumObjectHere>。如果可以编译,那就太好了。如果没有,那就有问题:

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

避免每次检查都声明新类型的另一种方式:

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"'.