Skip to content
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

Support inlining strings/const enum string types #3922

Closed
weswigham opened this issue Jul 18, 2015 · 5 comments
Closed

Support inlining strings/const enum string types #3922

weswigham opened this issue Jul 18, 2015 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@weswigham
Copy link
Member

Eg, we should allow

const enum Foo {
    A = "2",
    B = "3"
}

and compile it as we do numerical const enums.
Right now static classes end up getting used for this and don't get typechecked as an enum type - using an string enum clarifies types more, inlining the string clarifies the outputted JS more, and the feature in general could make writing .d.ts files for libs with event names or magic strings much easier. For example, String.normalize is currently defined as:

    /**
      * Returns the String value result of normalizing the string into the normalization form 
      * named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
      * @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default
      * is "NFC"
      */
    normalize(form?: string): string;

We could get better intellisense and clearer, more self-documenting code with something like so:

const enum UnicodeNormalizationForm {
    NFC = "NFC",
    NFD = "NFD",
    NFKC = "NFKC",
    NFKD = "NFKD",
}

class String {
    normalize(form?: UnicodeNormalizationForm): string;
}

It could also be worth discussing allowing enums in general to be based on arbitrary base types (or at least string/symbol based types, since those are valid object keys), similar to how enums can be based on any numeric type in C#.

Also, in case anyone wasn't aware, for non-const string enums we already emit (mostly) correct code, despite it (technically) being an error to do so - the following compiles without error:

enum Foo {
    A = <any>"A",
    B = <any>"B"
}

console.log(Foo.A);

to

var Foo;
(function (Foo) {
    Foo[Foo['A'] = 'A'] = 'A';
    Foo[Foo['B'] = 'B'] = 'B';
}(Foo || (Foo = {})));
console.log(Foo.A);

without the <any> casts its a type error to do so, however. (And it's pretty simple as to why you probably shouldn't do this - you can corrupt the enum like so):

enum Foo {
    A = <any>"0",
    B = <any>"1",
    C = <any>"A"
}

Foo.A has value "C" rather than "0" in this scenario, thanks to how the emit for enums currently works. However, with const enums this should never be an issue, so I believe that we should start with allowing string const enums.

@danquirk
Copy link
Member

See #3192 and/or #1206

@danquirk danquirk added the Duplicate An existing issue was already created label Jul 20, 2015
@loretoparisi
Copy link

@weswigham so using like

export enum ErrorHint {
    ERROR_HINT_DEFAULT = <any>'',
    ERROR_HINT_NOTAUTHORIZED = <any>'not_authorized', // unauthorized
    ERROR_HINT_SERVERERROR = <any>'server_error',
}

should not be used in any case since it is an error at runtime or - better - it is a side effect?

@weswigham
Copy link
Member Author

With what we currently have in master, you can do this, instead:

export namespace ErrorHint {
  export const ERROR_HINT_DEFAULT: '' = '';
  export type ERROR_HINT_DEFAULT = '';
  export const ERROR_HINT_NOTAUTHORIZED: 'not_authorized' = 'not_authorized';
  export type ERROR_HINT_NOTAUTHORIZED = 'not_authorized';
  export const ERROR_HINT_SERVERERROR: 'server_error' = 'server_error';
  export type ERROR_HINT_SERVERERROR = 'server_error';
}
export type ErrorHint = ErrorHint.ERROR_HINT_DEFAULT | ErrorHint.ERROR_HINT_NOTAUTHORIZED | ErrorHint.ERROR_HINT_SERVERERROR;

It's a bit verbose/repetitive, but it works really well, thanks to string literal types. (And looks/functions just like you'd expect an enum of strings to)

@loretoparisi
Copy link

ah so like the example I was looking at!

namespace S {
  export const A: "A" = "A";
  export type A = "A";
  export const B: "X" = "X";
  export type B = "X";
  export const C: "C" = "C";
  export type C = "C";

  // Imagine this were possible
  [s: string]: S;
}
type S = S.A | S.B | S.C;

without the [s: string]: S;

ok thank you it's ok for now!

@zspitz
Copy link
Contributor

zspitz commented Jan 30, 2017

@weswigham @loretoparisi Correct me if I am wrong, but this:

export namespace ErrorHint {
  export const ERROR_HINT_DEFAULT: '' = '';
  export type ERROR_HINT_DEFAULT = '';
  export const ERROR_HINT_NOTAUTHORIZED: 'not_authorized' = 'not_authorized';
  export type ERROR_HINT_NOTAUTHORIZED = 'not_authorized';
  export const ERROR_HINT_SERVERERROR: 'server_error' = 'server_error';
  export type ERROR_HINT_SERVERERROR = 'server_error';
}
export type ErrorHint = ErrorHint.ERROR_HINT_DEFAULT | ErrorHint.ERROR_HINT_NOTAUTHORIZED | ErrorHint.ERROR_HINT_SERVERERROR;

cannot be used in a declaration file, because unlike regular const enums, there is no inlining ivolved..

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants