镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Proposal: String enums #3192

Description

@jbondc

A string enum is inferred by looking at the constant value of the initializer of its first member:

enum stringsInferred {
   a = "a",
   b, // "b"
   c  // "c"
}

Or it is declared using a string index:

enum stringsDeclared {
   [prop: string] : string;
   a, // "a"
   B, // "B"
   c  // "c"
}

The auto initialized value on an enum is its property name.

Inferred enum special case

enum stringsLowerCase {
   Shoes = "shoes", // If the first member has lowercase chars, then lowercase all other member values
   Boots, // "boots"
   Feet  // "feet"
}

Activity

  1. mariusschulz commented on Jun 6, 2015

    @mariusschulz
    Contributor

    👍

    We should definitely be able to use enums with string values. Even a const enum with compile-time string literals (that are always safe to inline) would be helpful already, mainly for …

    • restricting variables to a set of known string values, and
    • avoiding misspellings.

    I'm not sure the automagical upper- and lowercasing described underneath Inferred enum special cases is such a good idea, though. I'd rather spell all cases exactly like they should be stringified:

    enum stringsUpperCase {
       Shoes = "SHOES",  // "SHOES"
       BOOTS,            // "BOOTS"
       Feet              // "Feet"
    }
  2. dead-claudia commented on Jul 11, 2015

    @dead-claudia

    Can this be generalized to other types (with the constraint that all types must be initialized - no value can be inferred)?

  3. dead-claudia commented on Jul 14, 2015

    @dead-claudia

    Jon (@jbondc)

    I agree, and this could be the other special case (where value inference already exists for numbers). Also, FWIW, I made a post on that bug with a few more specific suggestions IIRC. It just wasn't on the top of my mind at the time.

  4. dead-claudia commented on Jul 16, 2015

    @dead-claudia

    Jon (@jbondc)

    And I feel this syntax is a little repetitive in the [prop: string]: string part. Couldn't the syntax be modified to have the type after the enum name, instead of in the body? Maybe this syntax, to borrow your example?

    enum strings : string {
       Shoes,           // "Shoes"
       BOOTS,            // "BOOTS"
       Feet              // "Feet"
    }
  5. dead-claudia commented on Jul 16, 2015

    @dead-claudia

    Jon (@jbondc) Any advantages to that over my recent proposal? I don't think that regex-based solution is really that helpful in practice. All that regex does there is enforce type names, something better left to a linter as opposed to the main compiler. And it looks a little out of place compared to the enum entries. And as you said in the other thread, it will complicate the parser and compiler a little, likely more than it should.

    And as for extending enums, that's not very good practice, anyways. And it's generally not possible among other TypeScript users. Mine allows for such extensions, but you can only truly extend them in the same module or private namespace declaration, unless you're writing a definition file. That's because of the module wrapper emitted around every namespace declaration. So, for most purposes, you can't really extend them in the first place. Also, the spec currently requires that same-named declarations within the same root to add properties to the same enum.

  6. lazdmx commented on Jul 16, 2015

    @lazdmx

    👍 for string (const) enums

  7. added
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    on Jul 20, 2015
  8. rotemdan commented on Sep 4, 2015

    @rotemdan

    My current workaround looks something like this (this is a real-world, copy-and-paste code example):

    class Task {
      ..
      static State = { 
        Unstarted: "Unstarted", 
        Started: "Started", 
        Completed: "Completed", 
        Faulted: "Faulted", 
        Canceled: "Canceled"
      }
    }
    
    let task = new Task();
    ..
    if (task.state === Task.State.Completed) // The declared type of task.state is "string" here.
      console.log("Done!");

    This also gives a nice encapsulation into the class type that I find appealing (through the static modifier).

    Replicating this through enum would require several different enhancements. I will consider posting a separate proposal for class encapsulated enums if that hasn't been proposed before.

    I think having string enums would be very useful (maybe "necessary" should be the right word here). Automatic inferring of things like letters would be problematic because of locale and language issues (every language would have its own letters and ordering). Automatic lowercasing/uppercasing would also be impacted from similar issues (every language has different lower/uppercasing rules), and seems to me like a minor detail/enhancement at this point.

  9. mariusschulz commented on Sep 4, 2015

    @mariusschulz
    Contributor

    Maybe the string keyword would lend itself to be included in the declaration:

    string enum PromiseState {
        Pending,
        Fulfilled,
        Rejected
    }

    In the above example, the string values of the cases would equal their names. They could be overwritten like this:

    string enum PromiseState {
        Pending = "pending",
        Fulfilled = "fulfilled",
        Rejected = "rejected"
    }
  10. rotemdan commented on Sep 4, 2015

    @rotemdan

    This may turn out a bit long for a const enum that is also exported:

    export const string enum PromiseState {
        Pending,
        Fulfilled,
        Rejected
    }

    So maybe an alternative using some sort of a psuedo-generic type parameter?

    export const enum PromiseState<string> {
        Pending,
        Fulfilled,
        Rejected
    }

    Due to issues with different languages, alphabets, and locales it would be difficult to apply a correct conversion to lowercase/uppercase in many languages other than English. So just preserving the original casing and allowing custom ones through = seems like a reasonable compromise.

  11. Gambero81 commented on Sep 19, 2015

    @Gambero81

    Hi, what is the actual state of this proposal? can we have a roadmap to know when will be implemented in the language? Thanks

  12. dead-claudia commented on Sep 20, 2015

    @dead-claudia

    Claudio (@Gambero81) See #1206 for a more up-to-date version of this.

  13. dead-claudia commented on Sep 20, 2015

    @dead-claudia

    Can this be closed in favor of #1206? It's more general, which is better IMHO.

  14. dead-claudia commented on Sep 21, 2015

    @dead-claudia

    Union and intersection types would already exist with my proposal. You
    could achieve the same thing.

    On Sun, Sep 20, 2015, 09:17 Jon notifications@github.com wrote:

    IMPinball https://github.com/impinball No, added this here strictly
    for discussing a string enum & use cases.
    e.g. can you interest two string enums?

    enum a {
    hello = "hello"
    }
    enum b {
    world = "world"
    }
    let c = a & b;
    let d = typeof a & typeof b;

    —
    Reply to this email directly or view it on GitHub
    #3192 (comment)
    .

  15. 48 remaining items

  16. Gambero81 commented on Dec 7, 2016

    @Gambero81

    Now with typescript 2.1 and keyof, there is a better implementation of string enum? and what about inline version for const string enum?

  17. igrayson commented on Jan 4, 2017

    @igrayson

    Nahuel Greco (@nahuel)

    Per your third question:

    function mkenum3<X extends string>(...x:X[]):{[K in X]:K } {
      const o:any = {};
      for (const k in x)
        o[k] = k;
      return o;
    }
    
    type enumType<T> = T[keyof T];
    
    const Colors3  = mkenum3('Red', 'Green');
    type  Colors3  = enumType<typeof Colors3>;
  18. normalser commented on Jan 11, 2017

    @normalser

    Ian Grayson (@igrayson) Is there any way to modify your version so it behave the same as Nahuel Greco (@nahuel) mkenum2 ?

    let a2 = Colors2.Red // "Red"
    let a3 = Colors3.Red // string -> would love to have "Red"
  19. dphilipson commented on Jan 18, 2017

    @dphilipson

    Ian Grayson (@igrayson) Awesome solution! But I think your loop is incorrect. It should be

    function mkenum3<X extends string>(...x:X[]):{[K in X]:K } {
      const o:any = {};
      for (const k in x)
        o[x[k]] = x[k];
      return o;
    }
    

    that is, iterate over the values of the array not the keys.

    When I ran your version, the types were correct, but the runtime value of Colors3 was {"0": 0, "1", 1}.

  20. dphilipson commented on Jan 20, 2017

    @dphilipson

    I use the techniques here in several of my projects, so I turned them into an NPM module for easy access. See https://github.com/dphilipson/typescript-string-enums.

    I don't mean to claim credit for this solution, and I did my best to credit the users who came up with it in the readme. Hopefully others will find this useful.

  21. pelotom commented on Feb 27, 2017

    @pelotom

    The runtypes library allows defining a runtime type for a union of literals and then extracting both the static type as well as its enumerated values. Example usage:

    // Define the runtype
    const Day = Union(
      Literal('Sunday'),
      Literal('Monday'),
      Literal('Tuesday'),
      Literal('Wednesday'),
      Literal('Thursday'),
      Literal('Friday'),
      Literal('Saturday'),
    )
    
    // Extract the static type
    type Day = Static<typeof Day> // = 'Sunday' | 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday'
    
    // Extract enumerated literal values
    const days: Day[] = Day.alternatives.map(lit => lit.value)
    
    for (const day of days) {
      console.log(`Good morning, it's ${day}!`)
    }
  22. kimamula commented on Apr 25, 2017

    @kimamula
    Contributor

    With custom transformers introduced by #13940, you can create string enum from string literal types.

    import { enumerate } from 'ts-transformer-enumerate';
    
    type Colors = 'green' | 'yellow' | 'red';
    const Colors = enumerate<Colors>(); // type of Colors is { [K in Colors]: K }
    
    console.log(Colors.green); // 'green'
    console.log(Colors.yellow); // 'yellow'
    console.log(Colors.red); // 'red'

    The above code is compiled to the following JavaScript.

    var ts_transformer_enumerate_1 = require("ts-transformer-enumerate");
    var Colors = { green: "green", yellow: "yellow", red: "red" }; // type of Colors is { [K in Colors]: K }
    console.log(Colors.green); // 'green'
    console.log(Colors.yellow); // 'yellow'
    console.log(Colors.red); // 'red'

    See https://github.com/kimamula/ts-transformer-enumerate.

  23. ahejlsberg commented on May 1, 2017

    @ahejlsberg
    Member

    Implementation now available in #15486.

  24. RyanCavanaugh commented on May 1, 2017

    @RyanCavanaugh
    Member

    Let's track this at #1206

  25. added
    DuplicateAn existing issue was already created
    and removed
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    SuggestionAn idea for TypeScript
    on May 1, 2017
  26. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

DuplicateAn existing issue was already created

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions