So... You have two declarations, one of which is a real array that's allocated at runtime. Is that really better than an enum?
Not to mention, there's a slight mental overhead to parsing this. When I see this code, I might wonder if there's a reason for this to be an array. I might wonder if the order is intentional.
An enum has a more clear intent. My only complaint is that enums are not string-by-default, so we end up writing our variants twice:
Yes, I didn't intend to imply otherwise, but I could've elaborated.
Some people will argue a preference for string literal union types over enums because the string literal types don't have any runtime overhead. They just provide type safety at write-time and are bare strings at runtime. But as soon as you start adding arrays and custom type predicate functions to work with them, you're adding runtime objects, which removes that particular advantage over enums.
One minor advantage is that you can import the type on its own. If an external app just needs the types, it can import them without affecting its bundle at all.
> Substantially. Look at the generated code for an enum.
I'll give you that. It looks like the TS compiler (according to the playground site) spits out some code that's intended for maximum compatibility with older versions of JS, even when targeting newer versions (which makes sense, since nothing is technically wrong about it).
when, we would obviously write the following in modern JS:
"use strict";
const MyType = {
A: "a",
B: "b",
};
So that's a bit disappointing.
So, this could matter if you intend to actually read the emitted JS. If, however, you're TypeScript-only, this is more-or-less the same as reading the ASM spit out by your C compiler or the Java bytecode spit out by javac.
> Also, this approach does not suffer the problems described by the article.
This argument doesn't hold water, unless you're taking a philosophical stance. The argument is that most TypeScript features don't actually spit out JavaScript code and this one does.
But, if you're going to write an array that lists your variants (and then write code elsewhere to check if a string is contained by said array, etc), then "extra" JavaScript code is still being generated- it's just generated by you instead of the TypeScript compiler. Why should we care who generates the code?
This argument only works when we're comparing to writing a string literal union type and no other supporting code for that type. My comment was specifically addressing the case of writing an array to hold our literals instead of writing an enum, and I stand by my claim that an enum is better because it's the same runtime overhead, but more clearly communicates intent/semantics to your fellow TypeScript devs (including future-you).
The modern Javascript version of that would be the same as the compiled Typescript version, the const version doesn't do the same things.
The IIFE is creating an object only if it doesn't already exist, and adds "A" and "B" to it. "var" doesn't error on a redeclaration, so if MyType already existed you'll get a mashup of the two versions of it. Even if the const was switched to var in the second one, that would still be a straight replacement of the values instead of merging them.
I haven't used Typescript, but I imagine this style was used so enums could gain new values later in the code without having to worry about execution order.
Oh, good point. I forgot about var not getting mad at redeclaration. So the default TS implementation will merge an existing object with the newly defined fields. I'm too lazy to check, but I wonder what happens if MyType already exists and is a scalar, like a number...
I think I was still accidentally correct in saying that's what we'd write because who the hell actually WANTS the default behavior? :p
> > Also, this approach does not suffer the problems described by the article.
>
> This argument doesn't hold water, unless you're taking a philosophical stance.
Respectfully, philosophy has nothing to do with this.
The argument that the other person made does, in fact, hold significant water. There are extremely long discussions about it on the Typescript GH repo.
.
> The argument is that most TypeScript features don't actually spit out JavaScript code and this one does.
No, it isn't.
.
> then "extra" JavaScript code is still being generated
I never said extra code was a problem. I have no problem with this.
What I said was that I found the code emitted by the enumeration stack to be problematic. You seem to have inferred cause (incorrectly.)
.
> Why should we care who generates the code?
Do you believe that I think a compiler should not generate code?
I never said anything of that form.
Genuinely, it's difficult to hold a discussion with people who read so deeply between the lines that they come to bizarre conclusions, then think those conclusions belong to the person on the other end of the wire.
.
> This argument only works when we're comparing to writing a string literal union type and no other supporting code for that type.
You're not talking about the same argument that I am.
.
> but more clearly communicates intent/semantics to your fellow TypeScript devs (including future-you).
Your comment said two things. You said that you don't like the generated implementation for enums and then you said "this approach does not suffer the problems described by the article."
The article listed literally one reason to not use enums, and that reason is because it requires to compiler to produce JavaScript code. So, if that's not what you're talking about, then I have no idea what "problems described by the article" you could possibly be talking about.
With all of your complaining about my response, you still didn't explain it.
This is the way, because iterating enums produces odd results due to a bidirectional mapping.
I had always used enums in TS until this year, but union literals are better.
I create my own enums with const objects, compute the type based off the object's values. So very similar this, just with an object as the source instead of an array.
Iterating over an enum in TypeScript always felt like code smell to me because of the filtering code I'd have to write to deal with the bidirectional mapping.
I often use that approach but (especially when you're importing from a seperate package) the compiler will sometimes view MyType as just an alias for string and won't catch typos.