如何从 TypeScript 中的枚举值构建类型?

mol*_*tar 25 types typescript typescript-generics

鉴于以下情况:

enum FooKeys {
  FOO = 'foo',
  BAR = 'bar',
}
Run Code Online (Sandbox Code Playgroud)

我想制作一个这样的界面,但不是手动定义键,而是用枚举的值构建它。

interface Foo {
  foo: string
  bar: string
}
Run Code Online (Sandbox Code Playgroud)

TypeScript 可以实现这样的功能吗?

谢谢!

Fla*_*ken 74

\n

如何从 TypeScript 中的枚举值构建类型?

\n
\n

枚举可以保存字符串和数字值。

\n

对于字符串: \n您可以使用模板字符串从枚举值创建字符串并集

\n
enum FooKeys {\n  FOO = \'foo\',\n  BAR = \'bar\',\n}\n\ntype FooValues =`${FooKeys}`; // this equals \'foo\' |\xc2\xa0\'bar\'\n
Run Code Online (Sandbox Code Playgroud)\n

对于数字:我们可以以相同的方式创建字符串并集或者:\n从 TS 5.0 开始

\n
\n

TypeScript 5.0 通过为每个计算成员创建\n唯一类型,设法将所有枚举变成联合枚举。这意味着现在可以缩小所有枚举的范围,并将其成员也作为类型引用。

\n
\n

意思是:

\n
enum MagicNumbers {\na = 1,\nb = 42\n}\n\nconst numberA : MagicNumbers = 1;\nconst numberB : MagicNumbers = 2; // will raise an error since TS 5.0\n\n
Run Code Online (Sandbox Code Playgroud)\n

自己实验一下

\n

对于两者: \n结合以上内容,我们可以构建一个EnumAsUnion类型助手,如下所示

\n

枚举作为union.ts

\n
type StringValues<T> = {\n  [K in keyof T]: T[K] extends string ? T[K] : never;\n}[keyof T];\n\ntype NumberValues<T> = {\n  [K in keyof T]: T[K] extends number ? T[K] : never;\n}[keyof T];\n\n/**\n * Usage : type EnumValues = EnumAsUnion<typeof anEnum>\n */\ntype EnumAsUnion<T> = `${StringValues<T>}` | NumberValues<T>;\n
Run Code Online (Sandbox Code Playgroud)\n

例子:

\n
import { EnumAsUnion } from \'enum-as-union\';\n\nenum anEnum {\n  val1 = \'a\',\n  val2 = \'b\',\n  val3 = 1,\n  val4 = 2,\n}\n\ntype EnumValues = EnumAsUnion<typeof anEnum>;\n\nlet t: EnumValues;\nt = \'a\';\nt = \'b\';\nt = \'c\'; // error, as expected\n\nt = 1;\nt = 2;\nt = 3; // error, as expected\n
Run Code Online (Sandbox Code Playgroud)\n

在这里尝试一下

\n


jca*_*alz 43

是的,您可以使用枚举值作为键。您可以使用像标准库这样的映射类型Record<K, V>来防止重复:

enum FooKeys {
  FOO = 'foo',
  BAR = 'bar',
}

// probably all you need, but it's a type alias
type FooType = Record<FooKeys, string>;

// if you need an interface instead you can do this
interface FooInterface extends FooType {};
Run Code Online (Sandbox Code Playgroud)

您可以验证它是否有效:

declare const foo: FooInterface;
foo.foo; // okay
foo[FooKeys.FOO]; // okay

foo.bar; // okay
foo[FooKeys.BAR]; // okay

foo.baz; // error
Run Code Online (Sandbox Code Playgroud)

那对你有用吗?祝你好运!

  • 它是一个[实用程序类型](http://www.typescriptlang.org/docs/handbook/utility-types.html#recordkt),其在标准库中的定义在上面的答案中链接。 (5认同)

Aka*_*abu 9

[@hackape 的解决方案][1] 很棒,但我发现扩展他的解决方案的重复最少,如下所示:

type ReverseMap<T extends Record<keyof T, any>> = {
  [V in T[keyof T]]: {
    [K in keyof T]: T[K] extends V ? K : never;
  }[keyof T];
}

const Map = {
  'FOO': "foo" as "foo",
  'BAR': "bar" as "bar",
}

const reverseMap: ReverseMap<typeof Map> = Object.entries(Map).reduce((rMap, [k, v]) => {
  rMap[v] = k;
  return rMap;
}, {} as any);

export type Values = keyof typeof reverseMap; // 'foo' | 'bar';
Run Code Online (Sandbox Code Playgroud)

[此处][2]很好地解释了 ReverseMap 的实现

[1]: https: //stackoverflow.com/a/60768453/5519365 [2]: /sf/answers/3864662021/

更新:我找到了一个更简单的 ReverseMap 解决方案

const Obj = {
 FOO: 'foo',
 BAR: 'bar',
} as const;

type ReverseMap<T> = T[keyof T];

export type Values = ReverseMap<typeof Obj>; // 'foo' | 'bar';
Run Code Online (Sandbox Code Playgroud)


Fil*_*man 6

这回答了你的问题了吗?

enum FOO_BAR {
    F = "foo",
    B = "bar",
}

type FooType = Record<FOO_BAR, string>;

const obj: FooType = {
    // you can use enum values, better for refactoring
    [FOO_BAR.F]: "action foo",
    [FOO_BAR.B]: "action bar",

    // or use enum values
    // foo: "action foo",
    // bar: "action bar",
};

obj[FOO_BAR.F]; // -> "action foo"
obj["foo"];     // -> "action foo"

// If you want partial keys
type FooTypePartial = Partial<FooType>;

const objPartial: FooTypePartial = {
    [FOO_BAR.F]: "action foo",
};

objPartial["foo"]; // -> "action foo", may be undefined

Run Code Online (Sandbox Code Playgroud)