New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: String enums #3192

Closed
jbondc opened this Issue May 17, 2015 · 70 comments

Comments

Projects
None yet
@jbondc
Contributor

jbondc commented May 17, 2015

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"
}
@mariusschulz

This comment has been minimized.

Show comment
Hide comment
@mariusschulz

mariusschulz Jun 6, 2015

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"
}
Contributor

mariusschulz commented Jun 6, 2015

👍

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"
}
@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Jul 11, 2015

Contributor

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

Contributor

isiahmeadows commented Jul 11, 2015

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

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Jul 14, 2015

Contributor

@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.

Contributor

isiahmeadows commented Jul 14, 2015

@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.

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Jul 16, 2015

Contributor

@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"
}
Contributor

isiahmeadows commented Jul 16, 2015

@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"
}
@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Jul 16, 2015

Contributor

@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.

Contributor

isiahmeadows commented Jul 16, 2015

@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.

@lazdmx

This comment has been minimized.

Show comment
Hide comment
@lazdmx

lazdmx Jul 16, 2015

👍 for string (const) enums

lazdmx commented Jul 16, 2015

👍 for string (const) enums

@rotemdan

This comment has been minimized.

Show comment
Hide comment
@rotemdan

rotemdan Sep 4, 2015

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.

rotemdan commented Sep 4, 2015

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.

@mariusschulz

This comment has been minimized.

Show comment
Hide comment
@mariusschulz

mariusschulz Sep 4, 2015

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"
}
Contributor

mariusschulz commented Sep 4, 2015

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"
}
@rotemdan

This comment has been minimized.

Show comment
Hide comment
@rotemdan

rotemdan Sep 4, 2015

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.

rotemdan commented Sep 4, 2015

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.

@Gambero81

This comment has been minimized.

Show comment
Hide comment
@Gambero81

Gambero81 Sep 19, 2015

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

Gambero81 commented Sep 19, 2015

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

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Sep 20, 2015

Contributor

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

Contributor

isiahmeadows commented Sep 20, 2015

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

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Sep 20, 2015

Contributor

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

Contributor

isiahmeadows commented Sep 20, 2015

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

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Sep 21, 2015

Contributor

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)
.

Contributor

isiahmeadows commented Sep 21, 2015

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)
.

@vitrilo

This comment has been minimized.

Show comment
Hide comment
@vitrilo

vitrilo Sep 29, 2015

Using of String are really neat feature. In our 20k loc project we still use workarounds.
Apart, using objects as enum values (like in #1206 or http://stackoverflow.com/a/15491832/1224258) are risky - comparison fails after any Enums serialization/de-serialization or deepcopy (angular.copy).

vitrilo commented Sep 29, 2015

Using of String are really neat feature. In our 20k loc project we still use workarounds.
Apart, using objects as enum values (like in #1206 or http://stackoverflow.com/a/15491832/1224258) are risky - comparison fails after any Enums serialization/de-serialization or deepcopy (angular.copy).

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Sep 30, 2015

Contributor

Probably better if #1003 is done instead

Contributor

basarat commented Sep 30, 2015

Probably better if #1003 is done instead

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Sep 30, 2015

Contributor

@basarat I think both have their uses. A primitive enum (excluding symbols) could be seen as the union of their values in that sense. And also, I believe that should be expanded to also include numbers.

Contributor

isiahmeadows commented Sep 30, 2015

@basarat I think both have their uses. A primitive enum (excluding symbols) could be seen as the union of their values in that sense. And also, I believe that should be expanded to also include numbers.

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Sep 30, 2015

Contributor

I made a relevant response over there on #1003.

Contributor

isiahmeadows commented Sep 30, 2015

I made a relevant response over there on #1003.

@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Sep 30, 2015

Contributor

@jbondc I tried to include those, but I didn't get a very positive response from it.

Contributor

isiahmeadows commented Sep 30, 2015

@jbondc I tried to include those, but I didn't get a very positive response from it.

@vitrilo

This comment has been minimized.

Show comment
Hide comment
@vitrilo

vitrilo Sep 30, 2015

@basarat Thanks, I've just discovered related #1003, #1295. Great proposals/ideas!
My understanding #1003 (string literal union type) vs #3192 (string enums) is following:

  • String enums are simple, straightforward approach
  • #1003 - are more wider idea, which are may be both: multi-purpose (like for type guards) solution and space for funky/unwanted/corner cases
  • Syntax are RADICALLY different (which not obvious on first site):
    var var1 = MyEnum.Kind1; //string enum.
    var var2 = "kind1"; //string literal union type - NO compiler type-checking! 
    var var3:MyUnionEnum = "kind1"; //string literal union type with compiler type-checking 
  • Readability: IMHO, String enums have better readability. Moreover, in many languages enum types make uppercased to highlight limited possibilities and static nature.
  • IDE support. Tools are all for us! Possible, implementing auto-complete for #1003 (string literal union type) may be significantly harder inside IDEs comparing to #3192 (string enums). Not because of complexity of union-type concept, also because IDE should introduce in-natural auto-complete inside string literals: "kind1" (should suggest kind2,kind3.. if this is union type).

Apart, from all above, Javascript ecosystem generally encourage wide usage of string literals. As result, like in #1003, #1295 IDEs/Tools will be forced to support special meaning of string literals, like as enum constants/properties names etc, not just meaningless text to output.

vitrilo commented Sep 30, 2015

@basarat Thanks, I've just discovered related #1003, #1295. Great proposals/ideas!
My understanding #1003 (string literal union type) vs #3192 (string enums) is following:

  • String enums are simple, straightforward approach
  • #1003 - are more wider idea, which are may be both: multi-purpose (like for type guards) solution and space for funky/unwanted/corner cases
  • Syntax are RADICALLY different (which not obvious on first site):
    var var1 = MyEnum.Kind1; //string enum.
    var var2 = "kind1"; //string literal union type - NO compiler type-checking! 
    var var3:MyUnionEnum = "kind1"; //string literal union type with compiler type-checking 
  • Readability: IMHO, String enums have better readability. Moreover, in many languages enum types make uppercased to highlight limited possibilities and static nature.
  • IDE support. Tools are all for us! Possible, implementing auto-complete for #1003 (string literal union type) may be significantly harder inside IDEs comparing to #3192 (string enums). Not because of complexity of union-type concept, also because IDE should introduce in-natural auto-complete inside string literals: "kind1" (should suggest kind2,kind3.. if this is union type).

Apart, from all above, Javascript ecosystem generally encourage wide usage of string literals. As result, like in #1003, #1295 IDEs/Tools will be forced to support special meaning of string literals, like as enum constants/properties names etc, not just meaningless text to output.

@jtheoof

This comment has been minimized.

Show comment
Hide comment
@jtheoof

jtheoof Nov 20, 2015

👍 This feature would be great.

Right now, the enums are a bit counter intuitive from a JavaScript usage perspective. They work well for internal TypeScript code but not so much when code is used in third party apps.

It would be especially useful for bridging the gap between both worlds:

Example:

sdk.ts
--------

export enum Environments {
  SANDBOX,
  PRODUCTION
}

interface ClientOptions {
  id: string;
  secret: string;
  env?: Environments;
}

export class Client {
  constructor(public options: ClientOptions) {}

  hostname(): string {
    let part = 'sandbox';

    if (this.env === Environments.PRODUCTION) {
      part = 'api';
    }

    return `rest.${part}.example.com`;
  }
}

caller.js
----------

var client = new Client({
  id: 'id',
  secret: 'secret',
  env: 'production'
});

Right now, client.hostname() will fail because Environments.PRODUCTION === 1.

jtheoof commented Nov 20, 2015

👍 This feature would be great.

Right now, the enums are a bit counter intuitive from a JavaScript usage perspective. They work well for internal TypeScript code but not so much when code is used in third party apps.

It would be especially useful for bridging the gap between both worlds:

Example:

sdk.ts
--------

export enum Environments {
  SANDBOX,
  PRODUCTION
}

interface ClientOptions {
  id: string;
  secret: string;
  env?: Environments;
}

export class Client {
  constructor(public options: ClientOptions) {}

  hostname(): string {
    let part = 'sandbox';

    if (this.env === Environments.PRODUCTION) {
      part = 'api';
    }

    return `rest.${part}.example.com`;
  }
}

caller.js
----------

var client = new Client({
  id: 'id',
  secret: 'secret',
  env: 'production'
});

Right now, client.hostname() will fail because Environments.PRODUCTION === 1.

@mrcrowl

This comment has been minimized.

Show comment
Hide comment
@mrcrowl

mrcrowl commented Nov 26, 2015

👍

@olmobrutall

This comment has been minimized.

Show comment
Hide comment
@olmobrutall

olmobrutall Dec 16, 2015

My two cents while this gets approved:

Not that you can pretend this feature like this.

    export enum OrderType {
        Ascending = "Ascending" as any,
        Descending = "Descending" as any,
    }

The benefit is that then your API is better documented, asking for an OrderType instead of a dirty string.

The problem is that you need to repeat the identifier for the common case when declaring.

Also, when using it, you have to cast from string -> any -> OrdeType:

var orderType: OrderType = "Ascending"; //Error
var orderType: OrderType = "Ascending" as any; //OK

olmobrutall commented Dec 16, 2015

My two cents while this gets approved:

Not that you can pretend this feature like this.

    export enum OrderType {
        Ascending = "Ascending" as any,
        Descending = "Descending" as any,
    }

The benefit is that then your API is better documented, asking for an OrderType instead of a dirty string.

The problem is that you need to repeat the identifier for the common case when declaring.

Also, when using it, you have to cast from string -> any -> OrdeType:

var orderType: OrderType = "Ascending"; //Error
var orderType: OrderType = "Ascending" as any; //OK
@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Dec 16, 2015

Member

Edit: Maybe don't do this since string literal types are still in flux.


Here's a way you'll be able to emulate string enums in typescript@next. Taking a modified version of Esprima's Syntax object:

function strEnumify<T extends { [prop: string]: "" | string }>(obj: T) {
    return obj;
}

export const Syntax = strEnumify({
    AssignmentExpression: 'AssignmentExpression',
    AssignmentPattern: 'AssignmentPattern',
    ArrayExpression: 'ArrayExpression',
    ArrayPattern: 'ArrayPattern',
    ArrowFunctionExpression: 'ArrowFunctionExpression',
    BlockStatement: 'BlockStatement',
    BinaryExpression: 'BinaryExpression',
    BreakStatement: 'BreakStatement',
    CallExpression: 'CallExpression',
    CatchClause: 'CatchClause',
    ClassBody: 'ClassBody',
    ClassDeclaration: 'ClassDeclaration',
    ClassExpression: 'ClassExpression',
    // ...
    ThisExpression: 'ThisExpression',
    ThrowStatement: 'ThrowStatement',
    TryStatement: 'TryStatement',
    UnaryExpression: 'UnaryExpression',
    UpdateExpression: 'UpdateExpression',
    VariableDeclaration: 'VariableDeclaration',
    VariableDeclarator: 'VariableDeclarator',
    WhileStatement: 'WhileStatement',
    WithStatement: 'WithStatement',
    YieldExpression: 'YieldExpression'
});

image

(@ariya)

The problem is that you can't do something like the following:

let kind = Syntax.VariableDeclaration;
kind = Syntax.WithStatement;
Member

DanielRosenwasser commented Dec 16, 2015

Edit: Maybe don't do this since string literal types are still in flux.


Here's a way you'll be able to emulate string enums in typescript@next. Taking a modified version of Esprima's Syntax object:

function strEnumify<T extends { [prop: string]: "" | string }>(obj: T) {
    return obj;
}

export const Syntax = strEnumify({
    AssignmentExpression: 'AssignmentExpression',
    AssignmentPattern: 'AssignmentPattern',
    ArrayExpression: 'ArrayExpression',
    ArrayPattern: 'ArrayPattern',
    ArrowFunctionExpression: 'ArrowFunctionExpression',
    BlockStatement: 'BlockStatement',
    BinaryExpression: 'BinaryExpression',
    BreakStatement: 'BreakStatement',
    CallExpression: 'CallExpression',
    CatchClause: 'CatchClause',
    ClassBody: 'ClassBody',
    ClassDeclaration: 'ClassDeclaration',
    ClassExpression: 'ClassExpression',
    // ...
    ThisExpression: 'ThisExpression',
    ThrowStatement: 'ThrowStatement',
    TryStatement: 'TryStatement',
    UnaryExpression: 'UnaryExpression',
    UpdateExpression: 'UpdateExpression',
    VariableDeclaration: 'VariableDeclaration',
    VariableDeclarator: 'VariableDeclarator',
    WhileStatement: 'WhileStatement',
    WithStatement: 'WithStatement',
    YieldExpression: 'YieldExpression'
});

image

(@ariya)

The problem is that you can't do something like the following:

let kind = Syntax.VariableDeclaration;
kind = Syntax.WithStatement;
@vitrilo

This comment has been minimized.

Show comment
Hide comment
@vitrilo

vitrilo Dec 17, 2015

@olmobrutall thanks for perfect workaround! Well serialized/deserialized, apart from unreadable numeric enums inside json.
Below working even in v1.4:

  enum OrderType { Ascending = <any>"Ascending", Descending = <any>"Descending" }

vitrilo commented Dec 17, 2015

@olmobrutall thanks for perfect workaround! Well serialized/deserialized, apart from unreadable numeric enums inside json.
Below working even in v1.4:

  enum OrderType { Ascending = <any>"Ascending", Descending = <any>"Descending" }
@isiahmeadows

This comment has been minimized.

Show comment
Hide comment
@isiahmeadows

isiahmeadows Dec 18, 2015

Contributor

@DanielRosenwasser I would generally prefer to use const numeric enums where possible (a JS-to-ESTree AST parser couldn't for spec reasons) for that just for memory and performance reasons (both reduced for free). But I could see merit in a string enum to hold error messages like what jshint uses.

Contributor

isiahmeadows commented Dec 18, 2015

@DanielRosenwasser I would generally prefer to use const numeric enums where possible (a JS-to-ESTree AST parser couldn't for spec reasons) for that just for memory and performance reasons (both reduced for free). But I could see merit in a string enum to hold error messages like what jshint uses.

@Gambero81

This comment has been minimized.

Show comment
Hide comment
@Gambero81

Gambero81 Dec 18, 2015

@olmobrutall @DanielRosenwasser @vitrilo there workaround are good but a good feature of string enum should be to inlining string const enum without emit js as done with standard const enum, unfortunally for this porpouse all these workaround doesn't work

Gambero81 commented Dec 18, 2015

@olmobrutall @DanielRosenwasser @vitrilo there workaround are good but a good feature of string enum should be to inlining string const enum without emit js as done with standard const enum, unfortunally for this porpouse all these workaround doesn't work

@olmobrutall

This comment has been minimized.

Show comment
Hide comment
@olmobrutall

olmobrutall Dec 18, 2015

@Gambero81, of course having native support from the language will be the best solution, simplifying the declaration (no repetition for the simple cases), and the usage (no casting).

About const enums, we already have the syntax and the concept for normal enums, it will make sense just to extrapolate the same concepts for string enums.

export const enum Color extends string{
   red,
   blue,
   darkViolet= "dark-violet"
}

olmobrutall commented Dec 18, 2015

@Gambero81, of course having native support from the language will be the best solution, simplifying the declaration (no repetition for the simple cases), and the usage (no casting).

About const enums, we already have the syntax and the concept for normal enums, it will make sense just to extrapolate the same concepts for string enums.

export const enum Color extends string{
   red,
   blue,
   darkViolet= "dark-violet"
}
@hgoebl

This comment has been minimized.

Show comment
Hide comment
@hgoebl

hgoebl Jan 24, 2016

👍 current project: accessing REST service with generated TypeScript interfaces and enums. Enums are not usable, since service needs strings in JSON payload.

Is there a way to serialize / deserialize JSON and convert enum instances on-the-fly?

hgoebl commented Jan 24, 2016

👍 current project: accessing REST service with generated TypeScript interfaces and enums. Enums are not usable, since service needs strings in JSON payload.

Is there a way to serialize / deserialize JSON and convert enum instances on-the-fly?

@olmobrutall

This comment has been minimized.

Show comment
Hide comment
@olmobrutall

olmobrutall Jan 28, 2016

I've just found this today: https://blog.wearewizards.io/flow-and-typescript-part-2-typescript

TLDR; Typescript is better than flow but has no string enums 👎

The only issues we have encountered so far (and would love help to resolve!) are:

  • webpack building the previous version: adding an empty line and saving a file re-triggers a compilation but can be frustrating
  • no string enums: we want to keep human readable enum members but TypeScript only supports int enums

Any news on this issue? Any change this gets implemented.

olmobrutall commented Jan 28, 2016

I've just found this today: https://blog.wearewizards.io/flow-and-typescript-part-2-typescript

TLDR; Typescript is better than flow but has no string enums 👎

The only issues we have encountered so far (and would love help to resolve!) are:

  • webpack building the previous version: adding an empty line and saving a file re-triggers a compilation but can be frustrating
  • no string enums: we want to keep human readable enum members but TypeScript only supports int enums

Any news on this issue? Any change this gets implemented.

@Gambero81

This comment has been minimized.

Show comment
Hide comment
@Gambero81

Gambero81 Jan 28, 2016

I agree.. string enum is a MUST!! (in particular const string enum)

still hope to show it soon...

Gambero81 commented Jan 28, 2016

I agree.. string enum is a MUST!! (in particular const string enum)

still hope to show it soon...

@sampsyo

This comment has been minimized.

Show comment
Hide comment
@sampsyo

sampsyo Jan 28, 2016

I just read that the new TypeScript 1.8 beta has "string literal types": https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#string-literal-types

That seems to solve exactly this use case, right?

type Order = "Ascending" | "Descending";

sampsyo commented Jan 28, 2016

I just read that the new TypeScript 1.8 beta has "string literal types": https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#string-literal-types

That seems to solve exactly this use case, right?

type Order = "Ascending" | "Descending";
@cwmoo740

This comment has been minimized.

Show comment
Hide comment
@cwmoo740

cwmoo740 Sep 19, 2016

Using typescript@2.0.2 this seems to work for me:

namespace Colors {
    export type Red = 'Red';
    export type Blue = 'Blue';
    export const Red:Red = 'Red';
    export const Blue:Blue = 'Blue';
}
type Colors = Colors.Red | Colors.Blue;

It's ugly but then you can refer to it as a type and a string enum.

const myColors:{[key:string]: Colors, thirdThing: Colors.Red, fourthThing: colors.Blue} = 
{
    something: Colors.Red, 
    somethingElse: Colors.Blue, 
    thirdThing:'Red',
    fourthThing: Colors.Blue
};

cwmoo740 commented Sep 19, 2016

Using typescript@2.0.2 this seems to work for me:

namespace Colors {
    export type Red = 'Red';
    export type Blue = 'Blue';
    export const Red:Red = 'Red';
    export const Blue:Blue = 'Blue';
}
type Colors = Colors.Red | Colors.Blue;

It's ugly but then you can refer to it as a type and a string enum.

const myColors:{[key:string]: Colors, thirdThing: Colors.Red, fourthThing: colors.Blue} = 
{
    something: Colors.Red, 
    somethingElse: Colors.Blue, 
    thirdThing:'Red',
    fourthThing: Colors.Blue
};
@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Sep 19, 2016

Member

I think we can dupe this to #1206 since the only other type really being discussed there is string and there's some progress being made to some solution

Member

RyanCavanaugh commented Sep 19, 2016

I think we can dupe this to #1206 since the only other type really being discussed there is string and there's some progress being made to some solution

@malcompm

This comment has been minimized.

Show comment
Hide comment
@malcompm

malcompm Nov 15, 2016

Hi TS Team,
Seems like this is the most upvoted issue in this repository - so could we please at least get some update if this even get considered/done ? Seems like it does not even exist on the roadmap for Future https://github.com/Microsoft/TypeScript/wiki/Roadmap :(

malcompm commented Nov 15, 2016

Hi TS Team,
Seems like this is the most upvoted issue in this repository - so could we please at least get some update if this even get considered/done ? Seems like it does not even exist on the roadmap for Future https://github.com/Microsoft/TypeScript/wiki/Roadmap :(

@weswigham

This comment has been minimized.

Show comment
Hide comment
@weswigham

weswigham Nov 19, 2016

Member

With keyof applied to a namespace or object, you can now trivially use an object or namespace of string literals as a "string enum". Given that enums aren't on the ECMAScript proposal table, and TS has avoided adding any further non-typesystem non-ECMAScript syntax for many moons, I would not hold my breath on this. Instead, I'd write this (or a similar pattern with namespaces):

function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }
const Cardinal = mkenum({
  North: 'N',
  South: 'S',
  East: 'E',
  West: 'W'
});
type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];

Flexible, powerful, relatively DRY. All TS could do with further syntax is imply the type line from extra syntax in the declaration and infer literal types by default which, again, seems unlikely, given how little convince it offers over what is possible now, versus the extra risk of taking on new non-ECMAScript emit.

Member

weswigham commented Nov 19, 2016

With keyof applied to a namespace or object, you can now trivially use an object or namespace of string literals as a "string enum". Given that enums aren't on the ECMAScript proposal table, and TS has avoided adding any further non-typesystem non-ECMAScript syntax for many moons, I would not hold my breath on this. Instead, I'd write this (or a similar pattern with namespaces):

function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }
const Cardinal = mkenum({
  North: 'N',
  South: 'S',
  East: 'E',
  West: 'W'
});
type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];

Flexible, powerful, relatively DRY. All TS could do with further syntax is imply the type line from extra syntax in the declaration and infer literal types by default which, again, seems unlikely, given how little convince it offers over what is possible now, versus the extra risk of taking on new non-ECMAScript emit.

@normalser

This comment has been minimized.

Show comment
Hide comment
@normalser

normalser Nov 19, 2016

@weswigham Thanks for this great idea - is there any way to adjust it so that it isn't using widening types ?

As soon as one does:

let test = Cardinal.North

the type of test becomes string

normalser commented Nov 19, 2016

@weswigham Thanks for this great idea - is there any way to adjust it so that it isn't using widening types ?

As soon as one does:

let test = Cardinal.North

the type of test becomes string

@weswigham

This comment has been minimized.

Show comment
Hide comment
@weswigham

weswigham Nov 19, 2016

Member

@wallverb Use const or add a type annotation, otherwise, no. This is simply how string literal types are flowed now (the justification being: what is the use of assigning a single unit type to a mutable variable?).

So any of the following:

const test = Cardinal.North;
let test: Cardinal = Cardinal.North;
let test: 'N' | 'S' = Cardinal.North;

Depending on what, exactly, you intend to constrain the declaration to.

Member

weswigham commented Nov 19, 2016

@wallverb Use const or add a type annotation, otherwise, no. This is simply how string literal types are flowed now (the justification being: what is the use of assigning a single unit type to a mutable variable?).

So any of the following:

const test = Cardinal.North;
let test: Cardinal = Cardinal.North;
let test: 'N' | 'S' = Cardinal.North;

Depending on what, exactly, you intend to constrain the declaration to.

@normalser

This comment has been minimized.

Show comment
Hide comment
@normalser

normalser Nov 19, 2016

Or seems like one can do this (slightly less DRY but could prevent surprises and need to provide type annotations)

EDIT - Updated based on comment

const Cardinal = {
  North: 'N' as 'N',
  South: 'S' as 'S',
  East: 'E' as 'E',
  West: 'W' as 'W'
}
type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];

and use it in cases like (similar to regular enums):

interface Go {
  cardinal: Cardinal
  miles: number
}
function go(where:Go){...}

const where = {
   cardinal: Cardinal.North,
   miles: 50
} // No need to annotate

go(where)  // <- otherwise we would get string not assignable to N|S|E|W due to type widening

For me that's a good enough alternative to string enums. Thanks a lot @weswigham / TS Team

normalser commented Nov 19, 2016

Or seems like one can do this (slightly less DRY but could prevent surprises and need to provide type annotations)

EDIT - Updated based on comment

const Cardinal = {
  North: 'N' as 'N',
  South: 'S' as 'S',
  East: 'E' as 'E',
  West: 'W' as 'W'
}
type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];

and use it in cases like (similar to regular enums):

interface Go {
  cardinal: Cardinal
  miles: number
}
function go(where:Go){...}

const where = {
   cardinal: Cardinal.North,
   miles: 50
} // No need to annotate

go(where)  // <- otherwise we would get string not assignable to N|S|E|W due to type widening

For me that's a good enough alternative to string enums. Thanks a lot @weswigham / TS Team

@weswigham

This comment has been minimized.

Show comment
Hide comment
@weswigham

weswigham Nov 19, 2016

Member

You don't need the 'mkenum' function when you explicitly write the
typecasts like that. The function call only existed to make the compiler do
exactly that for you. ;)

On Sat, Nov 19, 2016, 11:25 AM wallverb notifications@github.com wrote:

Or seems like one can do this (slightly less DRY but could prevent
surprises and need to provide type annotations)

function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }const Cardinal = mkenum({
North: 'N' as 'N',
South: 'S' as 'S',
East: 'E' as 'E',
West: 'W' as 'W'
});type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];

and use it in cases like:

interface Go {
cardinal: Cardinal
miles: number
}function go(where:Go){...}
const a = {
cardinal: Cardinal.North,
miles: 50
} // No need to annotate
go(a) // <- otherwise we would get string not assignable to N|S|E|W due to type widening

For me that's a good enough alternative to string enums. Thanks a lot
@weswigham https://github.com/weswigham / TS Team


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#3192 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACzAMrXKNBMUMmJKrV6H1vcAeB4lu1Tiks5q_yLggaJpZM4Eds5F
.

Member

weswigham commented Nov 19, 2016

You don't need the 'mkenum' function when you explicitly write the
typecasts like that. The function call only existed to make the compiler do
exactly that for you. ;)

On Sat, Nov 19, 2016, 11:25 AM wallverb notifications@github.com wrote:

Or seems like one can do this (slightly less DRY but could prevent
surprises and need to provide type annotations)

function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }const Cardinal = mkenum({
North: 'N' as 'N',
South: 'S' as 'S',
East: 'E' as 'E',
West: 'W' as 'W'
});type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];

and use it in cases like:

interface Go {
cardinal: Cardinal
miles: number
}function go(where:Go){...}
const a = {
cardinal: Cardinal.North,
miles: 50
} // No need to annotate
go(a) // <- otherwise we would get string not assignable to N|S|E|W due to type widening

For me that's a good enough alternative to string enums. Thanks a lot
@weswigham https://github.com/weswigham / TS Team


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#3192 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACzAMrXKNBMUMmJKrV6H1vcAeB4lu1Tiks5q_yLggaJpZM4Eds5F
.

@mariusschulz

This comment has been minimized.

Show comment
Hide comment
@mariusschulz

mariusschulz Nov 19, 2016

Contributor

[…] TS has avoided adding any further non-typesystem non-ECMAScript syntax for many moons.

@weswigham While that is certainly true, the required syntax is already in place in large parts. We already have const enums. Allowing strings as values (rather than only numbers) would "only" be a relaxation of a constraint that currently exists.

Contributor

mariusschulz commented Nov 19, 2016

[…] TS has avoided adding any further non-typesystem non-ECMAScript syntax for many moons.

@weswigham While that is certainly true, the required syntax is already in place in large parts. We already have const enums. Allowing strings as values (rather than only numbers) would "only" be a relaxation of a constraint that currently exists.

@OliverJAsh

This comment has been minimized.

Show comment
Hide comment
@OliverJAsh

OliverJAsh Nov 22, 2016

@weswigham Given your example, how come the final line here fails?

function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }
const Cardinal = mkenum({
  North: 'N',
  South: 'S',
  East: 'E',
  West: 'W'
});
type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];
const p = Cardinal.East
const x = { y: Cardinal.East }
interface z { foo: Cardinal.East } // 'Cardinal' only refers to a type, but is being used as a value here.

OliverJAsh commented Nov 22, 2016

@weswigham Given your example, how come the final line here fails?

function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }
const Cardinal = mkenum({
  North: 'N',
  South: 'S',
  East: 'E',
  West: 'W'
});
type Cardinal = (typeof Cardinal)[keyof typeof Cardinal];
const p = Cardinal.East
const x = { y: Cardinal.East }
interface z { foo: Cardinal.East } // 'Cardinal' only refers to a type, but is being used as a value here.
@OliverJAsh

This comment has been minimized.

Show comment
Hide comment
@OliverJAsh

OliverJAsh Nov 22, 2016

Here's my workaround for string enums with type and value position support, until 2.1 lands with keyof:

// Boilerplate for string enums until 2.1 (keyof)
// String enums can be used in type and value positions.
// https://github.com/Microsoft/TypeScript/issues/3192#issuecomment-261720275
export class ActionTypes {
	public static Foo: 'Foo' = 'Foo'
}

interface FooAction extends ReduxAction {
	type: typeof ActionTypes.Foo
}

OliverJAsh commented Nov 22, 2016

Here's my workaround for string enums with type and value position support, until 2.1 lands with keyof:

// Boilerplate for string enums until 2.1 (keyof)
// String enums can be used in type and value positions.
// https://github.com/Microsoft/TypeScript/issues/3192#issuecomment-261720275
export class ActionTypes {
	public static Foo: 'Foo' = 'Foo'
}

interface FooAction extends ReduxAction {
	type: typeof ActionTypes.Foo
}
@Kovensky

This comment has been minimized.

Show comment
Hide comment
@Kovensky

Kovensky Nov 22, 2016

Contributor

@OliverJAsh The error in #3192 (comment) is correct. You are using an "enum value" (Cardinal.East) as if it was a type. The type Cardinal is the union type of all valid const Cardinal values' types (in this example, 'N' | 'S' | 'E' | 'W').

If you want to define a type that has a key that can only ever have the value Cardinal.East, you're going to have to use typeof Cardinal.East; but that's probably not what you mean to do. interface z { foo: Cardinal } should work and allow any of the Cardinal values.

Contributor

Kovensky commented Nov 22, 2016

@OliverJAsh The error in #3192 (comment) is correct. You are using an "enum value" (Cardinal.East) as if it was a type. The type Cardinal is the union type of all valid const Cardinal values' types (in this example, 'N' | 'S' | 'E' | 'W').

If you want to define a type that has a key that can only ever have the value Cardinal.East, you're going to have to use typeof Cardinal.East; but that's probably not what you mean to do. interface z { foo: Cardinal } should work and allow any of the Cardinal values.

@nahuel

This comment has been minimized.

Show comment
Hide comment
@nahuel

nahuel Dec 7, 2016

@weswigham just a note about the mkenum example from #3192 (comment) : the inferred type here will be string, that's a different behaviour from using an standard numeric enum:

enum NumericEnum {
    A = 1
}

let a = NumericEnum.A              // a inferred type will be NumericEnum, good
let b = Cardinal.North             // b inferred type will be string, bad, is too generic, so ...
let c : Cardinal = Cardinal.North  // to have an useful type we must explicitely type c

nahuel commented Dec 7, 2016

@weswigham just a note about the mkenum example from #3192 (comment) : the inferred type here will be string, that's a different behaviour from using an standard numeric enum:

enum NumericEnum {
    A = 1
}

let a = NumericEnum.A              // a inferred type will be NumericEnum, good
let b = Cardinal.North             // b inferred type will be string, bad, is too generic, so ...
let c : Cardinal = Cardinal.North  // to have an useful type we must explicitely type c
@nahuel

This comment has been minimized.

Show comment
Hide comment
@nahuel

nahuel Dec 7, 2016

Here is a mkenum version were you don't need to specify the strings twice (merged with the previous mkenum example):

// original version:
function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }
// new:
function mkenum2<X extends {[i:string] : any}, K extends string>(x : X ) 
  : {[K in (keyof X) ] : K}  {
    let o : any = {}
    for(let k in x) 
        o[k] = k;
    return o
}

// to define the type in a more succint way, for both versions:
export type enumType<T> = T[keyof T]

// define enums using the original and the new version:

const Colors  = mkenum({"Red"   : "Red", 
                        "Green" : "Green"});
type  Colors  = enumType<typeof Colors>

const Colors2 = mkenum2({"Red"   : 1, 
                         "Green" : 1});  // strings specified once, not twice
type  Colors2 = enumType<typeof Colors2>

let a  = Colors.Red       // string 
let a2 = Colors2.Red      // "Red"

let b  : Colors  = Colors.Red       // "Red" | "Green"
let b2 : Colors2 = Colors2.Red      // "Red" | "Green"

There is a little difference, the Colors constant is { Red : "Red", Green : "Green"} but the Colors2 constant is generated as { "Red" : "Red", "Green" : "Green"}.

Questions:

  1. What's generally better, the type inferred for a (string) or the one inferred for a2 (the "Red" literal)?
  2. If the one inferred for a is better, how do you change mkenum2 to generate a constant exactly like Colors to make a2 be inferred with the same type as a?
  3. Is possible a mkenum version that takes as parameter an array like ["Red", "Green"] ?

nahuel commented Dec 7, 2016

Here is a mkenum version were you don't need to specify the strings twice (merged with the previous mkenum example):

// original version:
function mkenum<T extends {[index: string]: U}, U extends string>(x: T) { return x; }
// new:
function mkenum2<X extends {[i:string] : any}, K extends string>(x : X ) 
  : {[K in (keyof X) ] : K}  {
    let o : any = {}
    for(let k in x) 
        o[k] = k;
    return o
}

// to define the type in a more succint way, for both versions:
export type enumType<T> = T[keyof T]

// define enums using the original and the new version:

const Colors  = mkenum({"Red"   : "Red", 
                        "Green" : "Green"});
type  Colors  = enumType<typeof Colors>

const Colors2 = mkenum2({"Red"   : 1, 
                         "Green" : 1});  // strings specified once, not twice
type  Colors2 = enumType<typeof Colors2>

let a  = Colors.Red       // string 
let a2 = Colors2.Red      // "Red"

let b  : Colors  = Colors.Red       // "Red" | "Green"
let b2 : Colors2 = Colors2.Red      // "Red" | "Green"

There is a little difference, the Colors constant is { Red : "Red", Green : "Green"} but the Colors2 constant is generated as { "Red" : "Red", "Green" : "Green"}.

Questions:

  1. What's generally better, the type inferred for a (string) or the one inferred for a2 (the "Red" literal)?
  2. If the one inferred for a is better, how do you change mkenum2 to generate a constant exactly like Colors to make a2 be inferred with the same type as a?
  3. Is possible a mkenum version that takes as parameter an array like ["Red", "Green"] ?
@Gambero81

This comment has been minimized.

Show comment
Hide comment
@Gambero81

Gambero81 Dec 7, 2016

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

Gambero81 commented Dec 7, 2016

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

@igrayson

This comment has been minimized.

Show comment
Hide comment
@igrayson

igrayson Jan 4, 2017

@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>;

igrayson commented Jan 4, 2017

@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>;
@normalser

This comment has been minimized.

Show comment
Hide comment
@normalser

normalser Jan 11, 2017

@igrayson Is there any way to modify your version so it behave the same as @nahuel mkenum2 ?

let a2 = Colors2.Red // "Red"
let a3 = Colors3.Red // string -> would love to have "Red"

normalser commented Jan 11, 2017

@igrayson Is there any way to modify your version so it behave the same as @nahuel mkenum2 ?

let a2 = Colors2.Red // "Red"
let a3 = Colors3.Red // string -> would love to have "Red"
@dphilipson

This comment has been minimized.

Show comment
Hide comment
@dphilipson

dphilipson Jan 18, 2017

@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}.

dphilipson commented Jan 18, 2017

@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}.

@dphilipson

This comment has been minimized.

Show comment
Hide comment
@dphilipson

dphilipson Jan 20, 2017

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.

dphilipson commented Jan 20, 2017

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.

@pelotom

This comment has been minimized.

Show comment
Hide comment
@pelotom

pelotom Feb 27, 2017

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}!`)
}

pelotom commented Feb 27, 2017

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}!`)
}
@kimamula

This comment has been minimized.

Show comment
Hide comment
@kimamula

kimamula Apr 25, 2017

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.

Contributor

kimamula commented Apr 25, 2017

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.

@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg May 1, 2017

Member

Implementation now available in #15486.

Member

ahejlsberg commented May 1, 2017

Implementation now available in #15486.

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh May 1, 2017

Member

Let's track this at #1206

Member

RyanCavanaugh commented May 1, 2017

Let's track this at #1206

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.