用字符串值创建一个枚举


Answers:


409

TypeScript 2.4

现在具有字符串枚举,因此您的代码可以正常工作:

enum E {
    hello = "hello",
    world = "world"
};

🌹

TypeScript 1.8

从TypeScript 1.8开始,您可以使用字符串文字类型为命名的字符串值(部分是用于枚举的值)提供可靠和安全的体验。

type Options = "hello" | "world";
var foo: Options;
foo = "hello"; // Okay 
foo = "asdf"; // Error!

更多:https : //www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types

旧版支持

TypeScript中的枚举是基于数字的。

您可以使用具有静态成员的类:

class E
{
    static hello = "hello";
    static world = "world"; 
}

您也可以简单地说:

var E = {
    hello: "hello",
    world: "world"
}

更新: 基于能够执行var test:E = E.hello;以下操作的要求满足了这一要求:

class E
{
    // boilerplate 
    constructor(public value:string){    
    }

    toString(){
        return this.value;
    }

    // values 
    static hello = new E("hello");
    static world = new E("world");
}

// Sample usage: 
var first:E = E.hello;
var second:E = E.world;
var third:E = E.hello;

console.log("First value is: "+ first);
console.log(first===third); 

几乎没有改善:toString(): string { return this.value; }
psulek

@psulek其实打字稿将推断出toString,因为它返回返回一个字符串this.value,并value为字符串类型。因此,您不能这样做,var x:number = E.hello.toString();并且如果这样做,var x = E.hello.toString();x也被string
推断

2
@BASarat这是真的,打字稿处理了这种情况,但是我的意思是每次我们知道它总是有带有返回类型的修饰方法,即使对于ts编译器来说不是必须的,但是对于我们的编码人员来说,当我们看到方法定义时会知道输入它返回。
psulek

@basarat是否有替换该get()方法的缺点return this.value?这样,它将在访问时返回字符串值,而不仅仅是在转换时返回toString()
约翰

@basarat如果您有几个类似的“枚举”,则由于结构类型的原因,编译器将不会区分它们-编译器将value在所有类型上看到成员并将它们视为可比较类型。您可以将value成员设为私有。这样,编译器将看不到它,也不会尝试应用结构化类型。
Kirill G.

113

在TypeScript的最新版本(1.0RC)中,可以使用如下枚举:

enum States {
    New,
    Active,
    Disabled
} 

// this will show message '0' which is number representation of enum member
alert(States.Active); 

// this will show message 'Disabled' as string representation of enum member
alert(States[States.Disabled]);

更新1

要从字符串值获取枚举成员的数字值,可以使用以下方法:

var str = "Active";
// this will show message '1'
alert(States[str]);

更新2

在最新的TypeScript 2.4中,引入了字符串枚举,如下所示:

enum ActionType {
    AddUser = "ADD_USER",
    DeleteUser = "DELETE_USER",
    RenameUser = "RENAME_USER",

    // Aliases
    RemoveUser = DeleteUser,
}

有关TypeScript 2.4的更多信息,请阅读MSDN上的博客


2
通常,首选此解决方案(因为它是一个真正的枚举),但是您对枚举名称是什么(因此称为“字符串”)有很大的限制。
JasonS 2015年

2
迄今为止的最佳解决方案。
阿隆·阿米尔2015年

2
有什么新消息吗?因为States[str]现在不工作。Type 'string' is not assignable to type 'States'
MrCroft

1
@MrCroft您可以使用:States[str as any]在当前(2.x)版本的Typescript中进行操作。
psulek

States [str]是我想要的。谢谢!
马丁·尼克尼克

81

TypeScript 2.4+

现在,您可以直接将字符串值分配给枚举成员:

enum Season {
    Winter = "winter",
    Spring = "spring",
    Summer = "summer",
    Fall = "fall"
}

有关更多信息,请参见#15486

TypeScript 1.8+

在TypeScript 1.8+中,您可以创建一个字符串文字类型来定义该类型,并为值列表创建一个具有相同名称的对象。它模仿字符串枚举的预期行为。

这是一个例子:

type MyStringEnum = "member1" | "member2";

const MyStringEnum = {
    Member1: "member1" as MyStringEnum,
    Member2: "member2" as MyStringEnum
};

它将像字符串枚举一样工作:

// implicit typing example
let myVariable = MyStringEnum.Member1; // ok
myVariable = "member2";                // ok
myVariable = "some other value";       // error, desired

// explict typing example
let myExplicitlyTypedVariable: MyStringEnum;
myExplicitlyTypedVariable = MyStringEnum.Member1; // ok
myExplicitlyTypedVariable = "member2";            // ok
myExplicitlyTypedVariable = "some other value";   // error, desired

确保键入对象中的所有字符串!如果不这样做,则在上面的第一个示例中,该变量将不会隐式键入为MyStringEnum


1
如何在声明文件中定义类似内容?
Zev Spitz

@ZevSpitz你可以做到
大卫Sherret

值得注意的是,使用当前的编译器,您可以在MyStringEnum中错误键入字符串值,并且不会抱怨。我一直在创建“ Enforcer”界面,以确保我的字符串始终有效。例如:interface MyStringEnumEnforcer {Member1:MyStringEnum,Member2:MyStringEnum}然后const MyStringEnum:MyStringEnumEnforcer = {Member1:“ member1”,Member2:“ member2”}这不允许输入错误的字符串,尽管编译器最终可能会为您工作最终是原始情况。这种方法有很多仪式,但我喜欢安全性。
jmorc


40

在TypeScript 0.9.0.1中,尽管发生编译器错误,但编译器仍可以将ts文件编译为js文件。该代码可以按我们预期的那样工作,Visual Studio 2012可以支持自动完成代码。

更新:

在语法上,TypeScript不允许我们创建带有字符串值的枚举,但是我们可以修改编译器:p

enum Link
{
    LEARN   =   <any>'/Tutorial',
    PLAY    =   <any>'/Playground',
    GET_IT  =   <any>'/#Download',
    RUN_IT  =   <any>'/Samples',
    JOIN_IN =   <any>'/#Community'
}

alert('Link.LEARN:    '                     + Link.LEARN);
alert('Link.PLAY:    '                      + Link.PLAY);
alert('Link.GET_IT:    '                    + Link.GET_IT);
alert('Link[\'/Samples\']:    Link.'        + Link['/Samples']);
alert('Link[\'/#Community\']    Link.'      + Link['/#Community']);

操场


1
不错,但是您不能在switch语句中使用这些枚举/常量,例如case Link.LEARN:会得到一个Cannot convert 'Link.LEARN' to 'string'构建错误。投射将不起作用。
Gone Coding

@TrueBlueAussie这对于运行TSC 1.0.0.0的我来说似乎很好用。另外,如果由于某种原因需要在case语句中放置常量/变量字符串,则将其强制转换为任意值都可以。
CodeAndCats 2014年

1
另外,谢谢@ zjc0816,我非常喜欢这个解决方案:)
CodeAndCats 2014年

那就是我想要的解决方案。
Murhaf Sousli '16

5
有趣的是,我想知道为什么TypeScript已经不仅仅支持枚举字符串了?很多人都想要这个(包括我)。
Hendy Irawan

23

TypeScript 2.1以上

TypeScript 2.1中引入的查找类型允许使用另一种模式来模拟字符串枚举:

// String enums in TypeScript 2.1
const EntityType = {
    Foo: 'Foo' as 'Foo',
    Bar: 'Bar' as 'Bar'
};

function doIt(entity: keyof typeof EntityType) {
    // ...
}

EntityType.Foo          // 'Foo'
doIt(EntityType.Foo);   // 👍
doIt(EntityType.Bar);   // 👍
doIt('Foo');            // 👍
doIt('Bad');            // 🙁 

TypeScript 2.4以上

在版本2.4中,TypeScript引入了对字符串枚举的本机支持,因此不需要上述解决方案。从TS文档:

enum Colors {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE",
}

如果枚举键名称不同于字符串值(例如,因为它很长),我将如何处理?
CletusW

没关系!在以下问题中解决了@Łukasz-pniewski的问题stackoverflow.com/a/42820134/1431146
CletusW

当尝试反向映射Enum时,tslint会在该String-Enum示例上引发错误:元素隐式具有“ any”类型,因为索引表达式不是“ number”类型。我猜问题是,在TS字符串枚举不能反向映射,请在该字符串枚举例如注释typescriptlang.org/docs/handbook/release-notes/... -这似乎为TS 2.4哪里是真实的引入了String-Enum,但在TS 2.6.2中也出现了错误。示例:Colors["RED"]将不起作用。任何解决方法的想法(JSON转换都需要)。
masi

19

为什么不使用访问枚举字符串的本机方法呢?

enum e {
  WHY,
  NOT,
  USE,
  NATIVE
}

e[e.WHY] // this returns string 'WHY'

2
这是我一直在寻找的答案,谢谢!其他解决方案是聪明的解决方法,但这很简单:)
M-2015年

19
这不能回答问题。问题不在于访问枚举的字符串。enum Why { Because = "You Can't", Always = "Do Things That Way." };)
James Wilkins

使用数值枚举时存在问题,例如0虚假,更难调试等
robmcm '17

@robmcm解决枚举∈{WHY = 1,NOT = 2,USE = 3,NATIVE = 4} E [e.WHY] //此返回字符串'为什么'
Mient-JAN Stelling

16

您可以在最新的TypeScript中使用字符串枚举:

enum e
{
    hello = <any>"hello",
    world = <any>"world"
};

资料来源:https : //blog.rsuter.com/how-to-implement-an-enum-with-string-values-in-typescript/


更新-2016

这些天来,我使用一些更健壮的方法来制作一组用于React的字符串,如下所示:

export class Messages
{
    static CouldNotValidateRequest: string = 'There was an error validating the request';
    static PasswordMustNotBeBlank: string = 'Password must not be blank';   
}

import {Messages as msg} from '../core/messages';
console.log(msg.PasswordMustNotBeBlank);

1
这是为我完成工作的最简洁的方式...至少直到我弄清楚如何更新脚手架以使用TS 1.8编译时
为止

但是,与此相关的一个问题是<string>e.hello触发错误。e.hello仍被编译器视为数字。<number>e.hello确实可以。有没有办法解决?我所能想到的就是<string><any>e.hello
RainingChain

另一个问题是当枚举成员等于枚举值时。例:enum Test { a = <any>"b", b = <any>"c", c = <any>"a" } Test.a === 'c'
RainingChain

我一直在使用这种方法。字符串枚举的石头。令人失望的是,编译器没有对字符串文字的第一类支持,但确实具有第二类支持。编译器实际上知道您何时使用过<any> hack,因为它会阻止您在.d.ts文件中使用它-对我来说,使用此“ hack”具有一定的合法性,因为编译器显然知道它,但不能完全阻止它。
CodeAndCats

顺便说一句,如果您想将一个字符串值与一个字符串枚举值进行比较,而不是强制转换为<any>then <string>,则要做:someStringValue == someEnumValue.toString()
CodeAndCats 2016年

10

这是一个相当干净的解决方案,允许使用TypeScript 2.0进行继承。我没有在较早的版本上尝试过。

奖励:该值可以是任何类型!

export class Enum<T> {
  public constructor(public readonly value: T) {}
  public toString() {
    return this.value.toString();
  }
}

export class PrimaryColor extends Enum<string> {
  public static readonly Red = new Enum('#FF0000');
  public static readonly Green = new Enum('#00FF00');
  public static readonly Blue = new Enum('#0000FF');
}

export class Color extends PrimaryColor {
  public static readonly White = new Enum('#FFFFFF');
  public static readonly Black = new Enum('#000000');
}

// Usage:

console.log(PrimaryColor.Red);
// Output: Enum { value: '#FF0000' }
console.log(Color.Red); // inherited!
// Output: Enum { value: '#FF0000' }
console.log(Color.Red.value); // we have to call .value to get the value.
// Output: #FF0000
console.log(Color.Red.toString()); // toString() works too.
// Output: #FF0000

class Thing {
  color: Color;
}

let thing: Thing = {
  color: Color.Red,
};

switch (thing.color) {
  case Color.Red: // ...
  case Color.White: // ...
}

1
好答案!我正在努力使一些类似枚举的对象具有继承支持。
DanielM

使用基于类的Enum的示例:goo.gl/SwH4zb(链接到TypeScript的游乐场)。
DanielM '16

8

一种怪异的方法是:-

呼叫状态

enum Status
{
    PENDING_SCHEDULING,
    SCHEDULED,
    CANCELLED,
    COMPLETED,
    IN_PROGRESS,
    FAILED,
    POSTPONED
}

export = Status

实用程序

static getEnumString(enum:any, key:any):string
{
    return enum[enum[key]];
}

如何使用

Utils.getEnumString(Status, Status.COMPLETED); // = "COMPLETED"

7

这对我有用:

class MyClass {
    static MyEnum: { Value1; Value2; Value3; }
    = {
        Value1: "Value1",
        Value2: "Value2",
        Value3: "Value3"
    };
}

要么

module MyModule {
    export var MyEnum: { Value1; Value2; Value3; }
    = {
        Value1: "Value1",
        Value2: "Value2",
        Value3: "Value3"
    };
}

8)

更新:发布此消息后不久,我发现了另一种方法,但是忘记发布更新(但是,上面已经有人提到过):

enum MyEnum {
    value1 = <any>"value1 ", 
    value2 = <any>"value2 ", 
    value3 = <any>"value3 " 
}

4

我只是声明一个接口,并使用该类型的变量访问枚举。使接口和枚举保持同步实际上很容易,因为TypeScript会抱怨枚举是否有所变化,就像这样。

错误TS2345:无法将类型'typeof EAbFlagEnum'的参数分配给类型'IAbFlagEnum'的参数。类型“ typeof EAbFlagEnum”中缺少属性“ Move”。

该方法的优点是不需要在各种情况下使用枚举(接口)进行类型转换,因此可以支持更多类型的情况,例如开关/案例。

// Declare a TypeScript enum using unique string 
//  (per hack mentioned by zjc0816)

enum EAbFlagEnum {
  None      = <any> "none",
  Select    = <any> "sel",
  Move      = <any> "mov",
  Edit      = <any> "edit",
  Sort      = <any> "sort",
  Clone     = <any> "clone"
}

// Create an interface that shadows the enum
//   and asserts that members are a type of any

interface IAbFlagEnum {
    None:   any;
    Select: any;
    Move:   any;
    Edit:   any;
    Sort:   any;
    Clone:  any;
}

// Export a variable of type interface that points to the enum

export var AbFlagEnum: IAbFlagEnum = EAbFlagEnum;

使用变量而不是枚举可产生所需的结果。

var strVal: string = AbFlagEnum.Edit;

switch (strVal) {
  case AbFlagEnum.Edit:
    break;
  case AbFlagEnum.Move:
    break;
  case AbFlagEnum.Clone
}

对我来说,标志是另一个必要条件,因此我创建了一个NPM模块,该模块添加到此示例中并包括测试。

https://github.com/djabraham/ts-enum-tools


这是我发现的唯一允许将定义与导入混合的答案。真好!您可以使用export default EAbFlagEnum as IAbFlagEnum;而不是重新声明变量。我还删除了<any>枚举中的演员表,它工作正常。
Guillaume F.

4

更新:TypeScript 3.4

您可以简单地使用as const

const AwesomeType = {
   Foo: "foo",
   Bar: "bar"
} as const;

TypeScript 2.1

这也可以通过这种方式完成。希望对别人有帮助。

const AwesomeType = {
    Foo: "foo" as "foo",
    Bar: "bar" as "bar"
};

type AwesomeType = (typeof AwesomeType)[keyof typeof AwesomeType];

console.log(AwesomeType.Bar); // returns bar
console.log(AwesomeType.Foo); // returns foo

function doSth(awesometype: AwesomeType) {
    console.log(awesometype);
}

doSth("foo") // return foo
doSth("bar") // returns bar
doSth(AwesomeType.Bar) // returns bar
doSth(AwesomeType.Foo) // returns foo
doSth('error') // does not compile

这正是我所需要的!它支持键名不同于字符串值,如您所显示的那样,使用大写/小写字母。谢谢!
CletusW '17

2

使用在typescript @ next中可用的自定义转换器(https://github.com/Microsoft/TypeScript/pull/13940),您可以使用字符串文字类型的字符串值创建类似于对象的枚举。

请查看我的npm包ts-transformer-enumerate

用法示例:

// The signature of `enumerate` here is `function enumerate<T extends string>(): { [K in T]: K };`
import { enumerate } from 'ts-transformer-enumerate';

type Colors = 'green' | 'yellow' | 'red';
const Colors = enumerate<Colors>();

console.log(Colors.green); // 'green'
console.log(Colors.yellow); // 'yellow'
console.log(Colors.red); // 'red'

2

打字稿<2.4

/** Utility function to create a K:V from a list of strings */
function strEnum<T extends string>(o: Array<T>): {[K in T]: K} {
  return o.reduce((res, key) => {
    res[key] = key;
    return res;
  }, Object.create(null));
}

/**
  * Sample create a string enum
  */

/** Create a K:V */
const Direction = strEnum([
  'North',
  'South',
  'East',
  'West'
])
/** Create a Type */
type Direction = keyof typeof Direction;

/** 
  * Sample using a string enum
  */
let sample: Direction;

sample = Direction.North; // Okay
sample = 'North'; // Okay
sample = 'AnythingElse'; // ERROR!

来自https://basarat.gitbooks.io/typescript/docs/types/literal-types.html

到源链接,您可以找到更多和更轻松的方法来完成字符串文字类型


2

有很多答案,但是我看不到任何完整的解决方案。可接受的答案以及的问题enum { this, one }是,它分散了您正使用的字符串值在许多文件中的分布。我也不很喜欢“更新”,它很复杂并且也没有利用类型。我认为Michael Bromley的答案是最正确的,但是它的界面有点麻烦,可以使用类型来完成。

我正在使用TypeScript 2.0。*这就是我要做的

export type Greeting = "hello" | "world";
export const Greeting : { hello: Greeting , world: Greeting } = {
    hello: "hello",
    world: "world"
};

let greet: Greeting = Greeting.hello

使用有用的IDE时,它还具有更好的类型/悬停信息。缺点是您必须两次编写字符串,但至少只有两个地方。


1

@basarat的回答很好。这是简化的示例,但可以使用一些扩展示例:

export type TMyEnumType = 'value1'|'value2';

export class MyEnumType {
    static VALUE1: TMyEnumType = 'value1';
    static VALUE2: TMyEnumType = 'value2';
}

console.log(MyEnumType.VALUE1); // 'value1'

const variable = MyEnumType.VALUE2; // it has the string value 'value2'

switch (variable) {
    case MyEnumType.VALUE1:
        // code...

    case MyEnumType.VALUE2:
        // code...
}

1

最近使用TypeScript 1.0.1面对此问题,并通过以下方式解决了该问题:

enum IEvents {
        /** A click on a product or product link for one or more products. */
        CLICK,
        /** A view of product details. */
        DETAIL,
        /** Adding one or more products to a shopping cart. */
        ADD,
        /** Remove one or more products from a shopping cart. */
        REMOVE,
        /** Initiating the checkout process for one or more products. */
        CHECKOUT,
        /** Sending the option value for a given checkout step. */
        CHECKOUT_OPTION,
        /** The sale of one or more products. */
        PURCHASE,
        /** The refund of one or more products. */
        REFUND,
        /** A click on an internal promotion. */
        PROMO_CLICK
}

var Events = [
        'click',
        'detail',
        'add',
        'remove',
        'checkout',
        'checkout_option',
        'purchase',
        'refund',
        'promo_click'
];

function stuff(event: IEvents):boolean {
        // event can now be only IEvents constants
        Events[event]; // event is actually a number that matches the index of the array
}
// stuff('click') won't work, it needs to be called using stuff(IEvents.CLICK)

0

我认为您应该尝试使用此方法,在这种情况下,变量的值将不会更改,它的工作原理类似于枚举,像类一样使用也可以工作,唯一的缺点是错误地可以更改静态变量的值,这就是我们不需要枚举。

namespace portal {

export namespace storageNames {

    export const appRegistration = 'appRegistration';
    export const accessToken = 'access_token';

  }
}


0
//to access the enum with its string value you can convert it to object 
//then you can convert enum to object with proberty 
//for Example :

enum days { "one" =3, "tow", "Three" }

let _days: any = days;

if (_days.one == days.one)
{ 
    alert(_days.one + ' | ' + _days[4]);
}


0

如果您想要的主要是简单的调试(使用相当的类型检查)并且不需要为枚举指定特殊值,这就是我正在做的事情:

export type Enum = { [index: number]: string } & { [key: string]: number } | Object;

/**
 * inplace update
 * */
export function enum_only_string<E extends Enum>(e: E) {
  Object.keys(e)
    .filter(i => Number.isFinite(+i))
    .forEach(i => {
      const s = e[i];
      e[s] = s;
      delete e[i];
    });
}

enum AuthType {
  phone, email, sms, password
}
enum_only_string(AuthType);

如果要支持旧版代码/数据存储,则可以保留数字键。

这样,您可以避免两次键入值。


0

非常非常非常简单的带有字符串的Enum(TypeScript 2.4)

import * from '../mylib'

export enum MESSAGES {
    ERROR_CHART_UNKNOWN,
    ERROR_2
}

export class Messages {
    public static get(id : MESSAGES){
        let message = ""
        switch (id) {
            case MESSAGES.ERROR_CHART_UNKNOWN :
                message = "The chart does not exist."
                break;
            case MESSAGES.ERROR_2 :
                message = "example."
                break;
        }
        return message
    }
}

function log(messageName:MESSAGES){
    console.log(Messages.get(messageName))
}

0

我已经尝试过像下面这样的TypeScript 1.5,它为我工作

module App.Constants {
   export enum e{
        Hello= ("Hello") as any,
World= ("World") as any
    }
}

0

我一直在寻找一种在打字稿枚举(v2.5)中实现描述的方法,这种模式对我有用:

export enum PriceTypes {
    Undefined = 0,
    UndefinedDescription = 'Undefined' as any,
    UserEntered = 1,
    UserEnteredDescription = 'User Entered' as any,
    GeneratedFromTrade = 2,
    GeneratedFromTradeDescription = 'Generated From Trade' as any,
    GeneratedFromFreeze = 3,
    GeneratedFromFreezeDescription = 'Generated Rom Freeze' as any
}

...

    GetDescription(e: any, id: number): string {
        return e[e[id].toString() + "Description"];
    }
    getPriceTypeDescription(price: IPricePoint): string {
        return this.GetDescription(PriceTypes, price.priceType);
    }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.