Repository navigation
Proposal: String enums #3192
Description
Activity
- addedSuggestionAn idea for TypeScriptAn idea for TypeScript
on May 28, 2015 mariusschulz commented
on Jun 6, 2015 ContributorMore actions👍
We should definitely be able to use enums with string values. Even a
const enumwith 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" }
Reacted by Josh Olson, Austin, Zev Spitz, Myrddin Emrys, CyrilleGuimezanes, Matheus MFCosta , sampaioletti, Andreas Wänqvist, Gregor Woiwode, Stefan Bauer and 24 moreCan this be generalized to other types (with the constraint that all types must be initialized - no value can be inferred)?
Reacted by Michael Messer, Ilia Choly, Lucas Basquerotto and Adam CmielI 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.
And I feel this syntax is a little repetitive in the
[prop: string]: stringpart. 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" }
Reacted by Jari Pennanen, Lucas Basquerotto, Quack, Arnaud Benhamdine, Jarrod Mosen, AA, Bao Bo and Matt FilionJon (@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.
👍 for string (const) enums
- addedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Jul 20, 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
staticmodifier).Replicating this through
enumwould 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 commented
on Sep 4, 2015 ContributorMore actionsMaybe the
stringkeyword 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" }
Reacted by acrazing, Frank Fajardo and TrukilThis may turn out a bit long for a
const enumthat 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.Reacted by Adam Cmiel and AJ RichardsonHi, what is the actual state of this proposal? can we have a roadmap to know when will be implemented in the language? Thanks
Claudio (@Gambero81) See #1206 for a more up-to-date version of this.
Can this be closed in favor of #1206? It's more general, which is better IMHO.
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)
.48 remaining items
Now with typescript 2.1 and keyof, there is a better implementation of string enum? and what about inline version for const string enum?
Reacted by Jan Žák, Kevin, Trina and Matt BanzPer 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>;
Reacted by Nahuel GrecoIan 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"
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
Colors3was{"0": 0, "1", 1}.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.
Reacted by Nahuel Greco, Ryan Cavanaugh, Jason Killian, AA, Santhosh, Frederik Aalund, Offirmo and gurra59Reacted by Offirmo and Michal PrzybysThe 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}!`) }
Reacted by Bao Bo and Alex WendlandWith 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.
Reacted by Oliver Joseph Ash, Danny Martini, Timofey Kachalov and Adrian SampsonImplementation now available in #15486.
Reacted by Seth Westphal, David Sherret, Adrian Sampson, Kent Wong, Zev Spitz, Timofey Kachalov, Heinrich Goebl, normalser, Mattias Buelens, Oliver Joseph Ash and 7 moreReacted by normalser, Oliver Joseph Ash, cevek, Chris Beech, Clayton Watts and TrinaReacted by normalser, Oliver Joseph Ash, Chris Beech and David AtenciaRyanCavanaugh commented
on May 1, 2017 MemberMore actionsLet's track this at #1206
- addedDuplicateAn existing issue was already createdAn existing issue was already createdand removedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.SuggestionAn idea for TypeScriptAn idea for TypeScript
on May 1, 2017 - locked and limited conversation to collaborators
on Jun 19, 2018
A string enum is inferred by looking at the constant value of the initializer of its first member:
Or it is declared using a string index:
The auto initialized value on an enum is its property name.
Inferred enum special case