[HN Gopher] TypeScript Features to Avoid
       ___________________________________________________________________
        
       TypeScript Features to Avoid
        
       Author : gary_bernhardt
       Score  : 212 points
       Date   : 2022-01-18 20:19 UTC (2 days ago)
        
 (HTM) web link (www.executeprogram.com)
 (TXT) w3m dump (www.executeprogram.com)
        
       | dmarchuk wrote:
       | I've never had any issues with enums, namespaces or private
       | keywords (can't speak for decorators as I haven't used them yet).
       | Although I do understand the reasoning for using the
       | '#somePrivateField' rather than 'private somePrivateField'.
       | 
       | But features like enums are part of the reason why we even have
       | tools like TypeScript, because JavaScript lacks these features.
        
         | wdb wrote:
         | I wish TypeScript somehow would use #-private fields underwater
         | when compiling code. `#field` is pretty confusing; I keep
         | thinking it's a comment and it looks ugly
        
           | dmarchuk wrote:
           | Although I do understand the reasoning in the article, I
           | completely agree with you.
           | 
           | Switching between Python and TypeScript/JavaScript all the
           | time, using '#' to define private field feels weird and I
           | personally prefer more explicit way of writing code. Plus
           | AFAIK the 'private someField' is common in other languages
           | (Java, Scala,...).
        
       | samwillis wrote:
       | What I like about TypeScript is that all these features are
       | optional, declaring that they should be avoided is going too far,
       | bus so would be saying that you should always use all features of
       | a language if it could apply.
       | 
       | Everything has its place, and for allot of the features of
       | TypeScript I think they are designed to be useful when you have a
       | large number of developers working on incredibly large codebases.
       | 
       | I suppose in some ways it's like C++, you can decide to fully
       | embrace all of its features, or code in a much more C like way,
       | just taking advantage of classes.
       | 
       | It comes down to personal/team preference, what works for you.
       | 
       | Personally with TypeScript I'm inclined to code in a "closer to
       | JavaScript" way (but taking full advantage of types obviously),
       | but would happily work in whatever style was prevailing in the
       | project.
        
       | chrismorgan wrote:
       | For people that don't see the problem and are happily using these
       | features, here's an explanation of the _second_ problem with
       | these sorts of features, additional to the "it's not just
       | JavaScript with types which is what the label said" reason which
       | is the focus of the article.
       | 
       | The real trouble occurs when TypeScript implements something
       | because that looks like the way things are heading, but then they
       | don't head that way, and JavaScript and TypeScript diverge
       | _incompatibly_. The type stuff is generally fairly safe, and
       | fundamentally necessary, but some of the other stuff they've
       | added isn't safe and isn't... _as_ necessary, at least.
       | Decorators are the current prime case of this divergence problem:
       | they added a feature because it was useful, lots of people wanted
       | it, and that was what people expected JavaScript to get before
       | long, and some were using already through a Babel extension but
       | people were sad about having to choose between nifty features
       | (Babel) and types (TypeScript); and then because they'd added
       | something not in JavaScript, why not go a bit further? and so
       | reflection metadata came along; and then... oh, turns out
       | decorators are actually heading in a completely different and
       | extremely incompatible direction in TC39 now, but people are
       | depending on our old way and PANIC! It's been a whole lot of
       | bother that will continue to cause even more trouble, especially
       | when they try to switch over to the new, if it gets stabilised--
       | that's going to be an extremely painful disaster for many
       | projects, because "experimental" or not, it's a widely-used
       | feature of TypeScript.
       | 
       | This is not the only such case; there's one other that caused a
       | lot of bother comparatively recently, but I can't think what it
       | was (I don't work in TypeScript much).
       | 
       | Sciter used what was loosely a fork of JavaScript when it
       | started, and diverged, for quite decent reasons in some cases I
       | will admit, but this divergence caused more and more trouble,
       | until recently they gave up and switched to JavaScript, ...
       | except with a couple of incompatible deviations already and more
       | on the table as probabilities. Sigh. I had hoped a lesson had
       | been learned.
       | 
       | So yeah, I'm content to call these things misfeatures. TypeScript
       | overstepped its bounds for reasons that seemed good at the time,
       | and may even have been important for social reasons at the time,
       | but you're better to avoid these features.
        
         | gherkinnn wrote:
         | I recently listened to an interview with Igor, Angular's
         | inventor.
         | 
         | In it he talked about how Angular 2 pushed decorators in to TS.
         | And to this day, Angular is the only major JS thing that I can
         | think of that uses decorators.
         | 
         | Creating that ng-abomination was not enough. No. Google also
         | had to poison a perfectly fine language.
        
           | holler wrote:
           | The latest versions of Ember.js (Octane) have built-in
           | decorator support and they're discussed in the RFC:
           | 
           | https://github.com/emberjs/rfcs/blob/master/text/0408-decora.
           | ..
           | 
           | https://guides.emberjs.com/release/in-depth-topics/native-
           | cl...
        
         | ameliaquining wrote:
         | Did the other feature have anything to do with modules? If so,
         | I can't really blame TypeScript too much for that, since the
         | JavaScript ecosystem is pretty fragmented there and TypeScript
         | has to support all the different things that are in use with a
         | reasonable interop story. Build tools that work purely with
         | JavaScript source code also have to deal with this problem.
         | 
         | Otherwise I'm not aware of any cases besides decorators where
         | TypeScript did something that was incompatible with a later
         | ECMAScript development.
        
           | chrismorgan wrote:
           | I really don't remember, sorry. I just scanned through
           | https://github.com/Microsoft/TypeScript/wiki/Breaking-
           | Change... and nothing jogged my memory; I'm wondering if it
           | actually _was_ something to do with decorators, though I
           | still think _probably_ not. I investigated a bit at the time
           | because I was looking into using TypeScript on a certain
           | project and the changes would probably affect me, but I
           | wasn't _actually_ using TypeScript at the time, and it didn't
           | stick in my memory. Something about some change being spread
           | over a few versions with deprecation followed by a silent
           | breaking change in the generated code, but I can't remember
           | what.
        
       | EMM_386 wrote:
       | I disagree with this. We have a reasonably large Angular
       | application that is only going to get much bigger (hard to define
       | what that means ... big telephony app with tens of thousands of
       | customers). I am the lead and architect.
       | 
       | We use enums and private keywords. With the private keywords, all
       | I care about is that it is logically correct. We use private when
       | things are truly private, i.e. they are only called from within
       | the same class and don't need to be made visible to the view
       | template or outside of the class. I honestly don't care _what_
       | this transpiles down to, the point for us at least is not to make
       | things  "truly private" (good luck with that in JavaScript). It's
       | simply to compiler-enforce rules. We also have ESLint to ensure
       | that our private fields are all below the public ones to keep
       | things nice and neat.
       | 
       | I also enforce that we actually make things as public although
       | that isn't needed, and I enforce returning void.
       | 
       | So instead of:
       | 
       | someMethod() {}
       | 
       | I have us use:
       | 
       | public someMethod(): void {}
       | 
       | Just to state what you intend.
       | 
       | I realize "I don't care what this transpiles down to" might
       | really irk some people, but I really don't. In our C# back-end I
       | am much more strict about this stuff, but in JS at the moment
       | given the standardization of the #private fields and the fact I
       | consider them really ugly, I honestly don't care. Just give me a
       | clean code base that enforces we can't reference private fields
       | and methods from our templates.
       | 
       | For enums, I recently wrote a method that does exactly what this
       | article says not to do, use it for GET, POST, and PUT.
       | 
       | What would be a _cleaner_ way to write this? If it has to be
       | refactored into something much  "uglier" I don't think I'd prefer
       | it.                   this.downloadService.fileWithProgress(url,
       | RequestType.post, ActionType.download, body)
       | 
       | This is a service I wrote that handles real-time progress (i.e.
       | show an accurate progress bar when downloading files). I think
       | this is clean and logical.
        
         | colejohnson66 wrote:
         | > I honestly don't care what this transpiles down to, the point
         | for us at least is not to make things "truly private" (good
         | luck with that in JavaScript). It's simply to compiler-enforce
         | rules.
         | 
         | It's not just JavaScript. With reflection in C# and Java, you
         | can mess around with private variables from outside the
         | classes. For Java, this can have some pretty interesting
         | results, such as _2+2 being equal to 5_.[0] The whole point of
         | compiler level annotations is to keep good programmers honest.
         | 
         | If some devious JavaScript developer wants to ruin your
         | library, that's their fault.
         | 
         | [0]: https://codegolf.stackexchange.com/a/28818/13944
        
           | pfooti wrote:
           | I mean, I don't know if you can do this with modern c++, but
           | I would occasionally do this when I was being aggressive in
           | my c++ tweaks:                   #define private public
           | #import <something.h>
           | 
           | then you can interact with your class private fields all you
           | want.
        
           | EMM_386 wrote:
           | Agreed, with reflection you can get around that also, but in
           | JS it's even less of a "real thing" at the moment and I don't
           | see that changing until the far future where we can drop some
           | of this legacy cruft.
        
           | lmm wrote:
           | > With reflection, you can mess around with private variables
           | from outside the classes.
           | 
           | Worth mentioning that you can disallow reflection via the
           | security manager, at least in earlier versions of the JVM.
        
         | BrandonM wrote:
         | From the article:
         | this.downloadService.fileWithProgress(url, 'POST', 'download',
         | body);              ...              public
         | fileWithProgress(url: string, reqType: RequestType, actionType:
         | ActionType, body: ...): void {             ...         }
         | ...         export type RequestType = 'GET' | 'POST' | ...;
         | export type ActionType = 'download' | ...;
        
       | 015a wrote:
       | The only point I take slight issue with is Avoiding Namespaces.
       | 
       | I can't speak for frontend code, but on the backend:
       | 
       | It feels at least somewhat obviously true that unique names are
       | good. Even if you aren't operating in a language which has global
       | imports (which is most nowadays), unique-as-possible names can
       | help disambiguate-at-a-glance something like:
       | const user: User = await getGoogleSsoUser();         // 100 lines
       | later...         console.log(user.microsoftId); // wait why isn't
       | that field available?         // ok i'll fix this         const
       | googleUser = await getGoogleSsoUser();         // obviously that
       | makes sense; but what about the type?         export function
       | getGoogleSsoUser(): Promise<User> {}         // wait... should
       | that return a Google API user object? or our own user model?
       | // let me scroll 200 lines up, ok its defined there, open that
       | file...          // or just:         export function
       | getGoogleSsoUser(): Promise<GoogleUser> {}
       | 
       | Contrived example of course, but it's a broader pattern I see
       | every day; there's a lot of overloaded terminology in
       | programming.
       | 
       | But this gets hairy really quickly.                   // google
       | will provide these types... but lets assume you're writing your
       | own         export type GoogleUserV1 = ...;         export type
       | GoogleAPIV1GetUserResponse = {           user: GoogleUserV1,
       | }         export type GoogleAPIV1ListUsersResponse = {
       | users: GoogleUserV1[],           count: number,         }
       | // ok lets import them         import { GoogleUserV1,
       | GoogleAPIV1GetUserResponse, GoogleAPIV1ListUsersResponse } from
       | "my/service/google";
       | 
       | First, the type names get really long, which makes them hard to
       | read at a glance. Second; this cost is replicated anytime someone
       | wants to import something. Third, they oftentimes become a
       | seemingly randomly ordered set of words written like a sentence;
       | why is it not "GoogleV1APIListUsersResponse" or
       | "GoogleAPIListUsersV1Response"?
       | 
       | We can solve the second problem by doing an old-style wildcard
       | import:                   import * as google from
       | "my/service/google";         const user: google.GoogleUserV1 =
       | await getGoogleSsoUser();
       | 
       | But this almost always ends up stuttering, because the producer
       | package still wants to guarantee unique-as-possible exported
       | names, as asserted above. So we made problem 1 worse, and did
       | nothing for problem 3.
       | 
       | With namespaces:                   export namespace Google {
       | export namespace User {             export type V1 { ... }
       | export type GetResponse { ... }             export type
       | ListResponse { ... }           }         }         // now to
       | import it         import { Google } from "my/service/google";
       | const user: Google.User.V1 = await getGoogleSsoUser();
       | 
       | The symbol, as a whole, isn't shorter. But, it's easier to read
       | (and write!). It also helps disambiguate where in the symbol each
       | component of the type's name should reside, when the producer
       | wants to for example add a new type or function.
       | 
       | The argument against presented by the article boils down to: it
       | creates unnecessary fluff in the emitted javascript. That's a
       | reasonable argument; it does. In practice, it's more nuanced.
       | First: I've never seen it cause an issue. So, premature
       | optimization, YMMV, etc. Second: the fluff is erased for types
       | anyway; so it only becomes an issue for functional code defined
       | like this (all of my examples were in types, but its easy to
       | imagine a Google.User.List function). Third, though not a direct
       | counterargument to the article: it's literally how Google
       | organizes the types we've been talking about [1] (though, how
       | they organize the functional code, I'm not sure).
       | 
       | [1]
       | https://github.com/DefinitelyTyped/DefinitelyTyped/blob/mast...
        
       | leodriesch wrote:
       | Despite the large amount of criticism in the comments here I
       | think that the point the article makes here is pretty valid.
       | 
       | The described features are not what TypeScript itself wants to
       | be, and I think if it wasn't for backwards compatibility the team
       | would remove some of them.
       | 
       | IIRC namespaces as well as the `import = ` syntax come from a
       | time where ESM wasn't a thing yet but a module system was very
       | much needed. So now that ESM can be used pretty much everywhere
       | namespaces can be avoided and therefore reduce the learning
       | surface of TypeScript.
       | 
       | Enums IMO have no advantage over union types with string
       | literals, e.g. `type Status = 'fulfilled' | 'pending' |
       | 'failed'`. They map way better to JSON and are generally easier
       | to understand, while still benefiting from type safety and typo
       | protection.
       | 
       | Well the private keyword is kind of like namespaces in that it
       | came from a time where the feature was needed/wanted, but the
       | EcmaScript spec was not moving fast enough. So they made their
       | own version of it which is now obsolete.
       | 
       | And for decorators, IIRC the decorator spec has already moved on
       | and TypeScript's implementation is no longer up to date with it.
       | And the spec itself is only stage 2 as mentioned in the article,
       | so I wouldn't recommend using decorators either, you will face
       | breaking changes with them some time in the future.
       | 
       | Furthermore, it is far more likely that you run into trouble when
       | using one of these features with a compiler that is not TSC, e.g.
       | esbuild or Babel. Decorators have never been working all that
       | well with the Babel plugin. Enums are probably fine here.
        
         | ameliaquining wrote:
         | Numeric enums are a lot more useful than string enums, in my
         | view.
        
           | goodoldneon wrote:
           | Just don't use numeric enums with implicit values. If you add
           | a member anywhere but the end of the enum, it'll change
           | existing members' values
        
           | leodriesch wrote:
           | I like string enums, since they are self-documenting, I guess
           | int enums are smaller when sent over network. Could you
           | expand on that?
        
             | spoiler wrote:
             | Why do you find them self documenting?
             | 
             | Usually it's just                   enum Method {
             | Get = "GET"           // ...         }
             | 
             | I usually always put doc comments on enums and their
             | variants.
             | 
             | Regarding why numeric ones are (only sometimes) more useful
             | than string values: bit flags comes to mind, faster
             | comparison of numbers than strings (not always tho...
             | unless this is a misconception, but I don't believe so),
             | you mentioned smaller bundle size already.
             | 
             | As for "auto" enums: the fact they're numbers now is an
             | implementation detail. They could be unique Symbols in a
             | future versions of typescript. You can do that manually now
             | too, but I'm talking about the automatic (without
             | assignment) syntax.
             | 
             | Regarding article: I... Half-agree. But I'd not completely
             | disregard/avoid enums. At the very least, they can be
             | useful to help model some behaviours/states in more
             | readable, accessible, and coherent way (Other comments went
             | into a more in-depth defence of enums, thought).
        
               | KuhlMensch wrote:
               | enum Method {           Get = "method/GET"           //
               | ...         }
               | 
               | The string value can be very helpful for the reader - be
               | it a human or log ingestor
        
             | ameliaquining wrote:
             | In the use cases where I most typically use enums, I don't
             | want to think about the question of runtime representation;
             | I just want a set of arbitrary symbols that are different
             | from one another. Implicitly initialized numeric enums do
             | this idiomatically and concisely.
        
               | leodriesch wrote:
               | Good point, I'm mostly doing web stuff so I always think
               | about how something looks in JSON in the network panel of
               | my browser.
               | 
               | If you just use the value within your code the runtime
               | representation does not matter, you're completely right.
        
               | piaste wrote:
               | If you truly don't care about the runtime representation,
               | then it sounds the idiomatic JS/TS construct you actually
               | want is Symbol()
               | 
               | https://developer.mozilla.org/en-
               | US/docs/Web/JavaScript/Refe...
               | 
               | https://www.typescriptlang.org/docs/handbook/2/everyday-
               | type...
        
               | JohnHaugeland wrote:
               | Symbol is an extremely heavy hammer. It's also difficult
               | to use correctly.
               | 
               | What value do you think it offers here?
        
               | ameliaquining wrote:
               | I agree that it might have been better for that to be the
               | TypeScript idiom, but it's not, due to the longer-
               | standing popularity and concision and language-level
               | support for enums. (Which couldn't have originally been
               | designed to be lowered to symbols, because at the time
               | symbols weren't widely-supported enough.)
        
               | rezonant wrote:
               | Symbols are very well supported in TS today. You even get
               | proper type enforcement and intellisense when using
               | symbols as the names of methods and properties, for
               | instance. Whether you should use them for enum-like
               | purposes or not has more to do with how you want to use
               | the values though. For instance using symbols for
               | defining numeric protocol values doesn't make sense. Same
               | for HTTP methods, where you are always ultimately passing
               | a string down to the http client/server interfaces
        
         | iLoveOncall wrote:
         | > Enums IMO have no advantage over union types with string
         | literals, e.g. `type Status = 'fulfilled' | 'pending' |
         | 'failed'`.
         | 
         | Well I would say having to not repeat and update your code
         | everywhere when you change or add a possible value is a pretty
         | big advantage.
        
           | pjerem wrote:
           | My IDE does this just fine (WebStorm, btw)
        
         | The_rationalist wrote:
        
         | [deleted]
        
         | Scarbutt wrote:
         | Though I have never seen anyone till now call TypeORM a fine
         | choice.
        
         | kesslern wrote:
         | One advantage of enums is that you can iterate over all
         | possible values of an enum, but not a union type.
        
           | cobertos wrote:
           | You can if you derive the union from a const array using
           | indexed types.                 const MyTypeValues = ['a',
           | 'b'] as const;       type MyType = typeof
           | MyTypeValues[number];
           | 
           | MyType is now a type 'a' | 'b'
        
             | lucasyvas wrote:
             | 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.
        
               | sli wrote:
               | 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.
        
             | jbbe wrote:
             | 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.
             | 
             | Am I missing something in how to use this?
        
               | Recursing wrote:
               | `as const` is the important bit there, see this
               | playground link https://www.typescriptlang.org/play?#code
               | /MYewdgzgLgBKlQIICd...
        
             | ragnese wrote:
             | 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:                   enum MyType {
             | A = 'a',             B = 'b',         }
        
               | nfw2 wrote:
               | 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.
        
               | Arnavion wrote:
               | >one of which is a real array that's allocated at
               | runtime.
               | 
               | To be clear, the enum is also defined at runtime. So this
               | specifically isn't a difference.
        
               | ragnese wrote:
               | 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.
        
               | JohnHaugeland wrote:
               | > Is that really better than an enum?
               | 
               | Substantially. Look at the generated code for an enum.
               | 
               | Also, this approach does not suffer the problems
               | described by the article.
        
               | ragnese wrote:
               | > 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).
               | 
               | It spits out:                   "use strict";         var
               | MyType;         (function (MyType) {
               | MyType["A"] = "a";             MyType["B"] = "b";
               | })(MyType || (MyType = {}));
               | 
               | 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).
        
               | JohnHaugeland wrote:
               | > > 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).
               | 
               | I write documentation.
        
           | [deleted]
        
       | dgb23 wrote:
       | > It may be a bit annoying to create many small files, but
       | modules have the same fundamental functionality as namespaces
       | without the potential downsides.
       | 
       | Do they?
       | 
       | It seems to me that namespaces are more powerful and convenient
       | than JS modules as they enable more structure.
       | 
       | I have only dabbled in TS and am not sure how useful they are
       | there. But I assume they would be similar to PHP/C#/Clojure
       | namespaces on a conceptual level.
        
       | Tabular-Iceberg wrote:
       | My problem with enums isn't so much that it breaks type-level
       | extension, it's that it breaks structural typing.
       | 
       | I don't particularly care what the generated JS looks like when I
       | do all my work in TS, but I do care that the type system of the
       | language I do work in works in a consistent manner.
        
         | cherryblossom00 wrote:
         | I find string enums useful when I want nominal typing. However,
         | I do think that it is a little confusing when most of the type
         | system is structural, except for string enums. What complicates
         | things further is that any number can be assigned to a number
         | enum without casting, which I understand is so enums can
         | represent bitfields (e.g. so Permissions.Read |
         | Permissions.Write is still of type Permissions instead of
         | number), but I wish numeric enums worked the same as string
         | enums and doing bitwise operations on these enums would return
         | the enum type and not a type-erased `number`.
         | 
         | I mostly agree with the rest of the article's recommendations,
         | but don't fully agree with their reasoning.
         | 
         | - Yes, namespaces should be avoided, but not because they
         | generate extra code. Namespaces are not recommended anymore and
         | modules (actual JS modules not TS' `module`) is the recommended
         | approach now.
         | 
         | - Yep, private fields are better for ensuring a field is truly
         | private.
         | 
         | - I think decorators are fine to use if you're prepared for
         | your code to potentially break in the future if they're
         | standardised. Developers use new/unstable features all the time
         | (e.g. stage 0/1 JS proposals via Babel, nightly Rust
         | toolchain). And I don't believe the lack of standardisation of
         | decorators should be a factor in deciding whether to use a
         | library that requires these.
        
       | mikojan wrote:
       | As long as you are not using "const enums" JavaScript/TypeScript
       | interop is not a real problem at all.
       | 
       | The private keyword is obviously preferable to "#" in
       | environments in which you are targeting older ES versions.
        
         | fuzzy2 wrote:
         | But const enums are gone after compiling. They're just
         | namespaced constants that are inlined. I don't see how this
         | could cause any problem.
        
           | mikojan wrote:
           | That's precisely the problem. It makes the enum invisible to
           | JavaScript users.
        
             | fuzzy2 wrote:
             | Yes, as it should be. There are no enums. Instead, you can
             | pass in any of the documented strings (or whatever the base
             | type is). Even inside TypeScript, an enum is really just a
             | union type on steroids.
             | 
             | When you expose TypeScript code to JavaScript consumers you
             | absolutely must validate all incoming data anyway, whatever
             | type you declare.
        
               | mikojan wrote:
               | This is not about trusting the information your users
               | provide. It is about making your APIs accessible to
               | users. If you hide some information, your API is less
               | accessible.
        
               | fuzzy2 wrote:
               | But it's just as hidden either way. The API consumer does
               | not know there's an enum that's supposed to go in there.
               | The only way to get that information is to read the
               | documentation.
        
       | davedx wrote:
       | The arguments not to use them are implementation details of the
       | tooling.
       | 
       | Well designed Abstractions are good, not bad...
        
       | matt7340 wrote:
       | I like these recommendations, especially around decorators, maybe
       | I'll start following them
       | 
       |  _reviews Angular app_
       | 
       | oh
        
         | leodriesch wrote:
         | Nest also does this, seems very strange to bet on such an
         | experimental feature. It's very convenient to use though.
        
       | Vinnl wrote:
       | This basically comes down to: avoid TypeScript features that
       | clash with TypeScript's design goals. Specifically, the one to:
       | 
       | > Avoid adding expression-level syntax.
       | 
       | https://github.com/Microsoft/TypeScript/wiki/TypeScript-Desi...
       | 
       | And that makes sense to me. IMHO it's best to consider TypeScript
       | as a tool that aims to help you write JavaScript by catching
       | common errors; it's more like a linter than a separate language
       | in that regard, and is what sets it apart from something like
       | CoffeeScript, and helps it avoid falling into the same traps.
        
         | msoad wrote:
         | Yes, I think if they could the would remove namespaces and
         | enums
        
         | wk_end wrote:
         | > it's more like a linter than a separate language
         | 
         | I wish people wouldn't think like this; it's very possible to
         | write perfectly acceptable JavaScript that's fundamentally
         | terrible TypeScript. By "fundamentally terrible", I mean
         | unnecessarily untypeable, or unnecessarily difficult to type.
         | If you're just writing your usual JavaScript without thinking
         | about the types just figuring you'll "lint" it later with tsc
         | to catch some bugs, if you aren't thinking in TypeScript from
         | the jump, you're likely to make your life much more unpleasant
         | down the line. It's similar to the relationship between C and
         | C++.
        
       | baryphonic wrote:
       | > 1. Avoid Enums
       | 
       | What? This is IMO bad advice. Having a sum type is quite handy
       | for general type-checking, at least insofar as the type truly is
       | an enumerated type (i.e. all possible values are known at design-
       | time). There have been times when TypeScript enums have been
       | indispensable to me when declaring the external interface to some
       | client-facing API. Whatever the API boundary is, a sum type is
       | useful.
       | 
       | Also, TypeScript gives general intersection types, which is quite
       | rare among its peers. (What I wouldn't give some days for mypy to
       | have intersection types, or bivariant functions, or conditional
       | types, or ...)
       | 
       | The only other impetus for this post I can imagine is some weird
       | desire to see typescript as a strictly separate layer above
       | JavaScript that "could be removed" if we wanted it to be. I
       | suppose that was the project's original telos, but today the
       | abstraction is leaky in a few places. I'm a world where JSX is
       | common and radically departs from what would be considered normal
       | JS, I don't see a problem with TypeScript being leaky here and
       | there. Hell, I'd prefer TS to be leakier and add opt-in runtime
       | checking (i.e. code gen), because it would make my life easier in
       | certain instances.
        
         | shadowgovt wrote:
         | Based on how two of the four points key into it, I think the
         | author is stuck using a tool in their tool chain that doesn't
         | support TypeScript and has been bitten by that tool doing
         | something Byzantine in response to TypeScript-generated code.
         | 
         | That's a problem a lot of developers won't have. I've covered a
         | lot of ground without ever finding a tool that either hasn't
         | been retrofit to support TypeScript or that has issues that are
         | tickled by TypeScript generated code. My blunt recommendation
         | if somebody hits that problem is to find a better tool.
        
         | jannes wrote:
         | The TypeScript ecosystem is bigger than just the official
         | compiler, though. Many projects use alternative compilers like
         | esbuild or @babel/preset-typescript for their main pipeline and
         | only use the official TypeScript compiler as a typechecker
         | (with `--noEmit`, during CI).
         | 
         | It's true that enums are harder to support for alternative
         | compilers. Especially `const enum` seems to be harder.
        
           | jcelerier wrote:
           | yes, we should also restrict every C++ feature to what
           | Borland Turbo C++ 3 supports too, since there are still
           | school that teach that making it part of the C++ ecosystem.
           | 
           | Actually, as Amish communities are part of the human
           | ecosystem, maybe we should also restrict anything we build in
           | the real world to something that can be useful to the Amish
           | and stop building anything requiring a power grid ?
        
           | oefrha wrote:
           | > It's true that enums are harder to support for alternative
           | compilers.
           | 
           | As long as it is supported -- and at least esbuild does, who
           | cares (as a user). Should I start avoiding every JS feature
           | that is hard to implement in alternative JS runtimes?
        
             | jannes wrote:
             | I don't know why you should care if it doesn't affect you.
             | 
             | At least TypeScript cares enough to have added the
             | `isolatedModules` and the `preserveValueImports` flags:
             | 
             | https://www.typescriptlang.org/tsconfig#isolatedModules
             | 
             | https://www.typescriptlang.org/tsconfig#preserveValueImport
             | s
        
         | activitypea wrote:
         | What are the advantages of enums over a string literal union? I
         | can only see disadvantages.
        
           | brightstep wrote:
           | In some cases you can use it to define string arguments to an
           | external library. Maybe table names to an ORM or a well-known
           | file path. This helps because you get autocomplete from
           | typing `MyEnum.` and seeing options, even though the external
           | function takes a plain string.
        
             | afavour wrote:
             | All of that is possible with a string literal union though,
             | surely?
        
               | brightstep wrote:
               | It's not possible to get the autocomplete unless the
               | function/method you're calling takes a union. Many
               | libraries can't do that because they need to be flexible
               | in what they receive. So yeah, you can define a union,
               | but editors don't have the context to know your union
               | applies to the call.
        
           | DrJokepu wrote:
           | If you have to change one of the underlying values, you only
           | need to change it in one place. Of course, you could just use
           | constants, but then you'll just have a const enum with extra
           | steps.
        
             | [deleted]
        
           | ragnese wrote:
           | Unfortunately, neither (string) enums nor string literal
           | unions are supersets of each other when it comes to
           | functionality. But, IMO, string-only enums are generally
           | better.
           | 
           | You can't test if a given string is an element of a union
           | without writing a custom helper and/or allocating a runtime
           | array of the values.
           | 
           | On the other hand, if you don't need to test unknown strings,
           | then union types disappear at compile time, whereas enums are
           | compiled to real, runtime, objects.
           | 
           | Enums don't look like naked strings in the code. Frankly,
           | it's just nicer on my brain to see `return Color.Red` than
           | `return "red"` and wonder if "red" is just some random text
           | or if it has semantic meaning. Hopefully your IDE is smart
           | enough to take you to where "red" is defined as part of a
           | union type when you want to see other options. Granted- the
           | most popular editors ARE smart enough to do that, but that
           | doesn't help when just reading code with my eyes instead of
           | my hands, or in patches/diffs.
        
         | dbrgn wrote:
         | Unfortunately TypeScript's enums have various shortcomings
         | compared to powerful sum types in languages like Rust:
         | 
         | - https://stackoverflow.com/questions/40275832/typescript-
         | has-...
         | 
         | - https://github.com/microsoft/TypeScript/issues/32690
         | 
         | In a TS codebase I'm currently working on, we have the policy
         | of never using "plain" TS enums. Instead, we have a tool that
         | generates our own enum objects using a schema and a generator
         | script for the cases where we want "rich" enums. For other use
         | cases, we use string unions a lot (in combination with a helper
         | function that allows exhaustive matching).
        
           | IshKebab wrote:
           | Rust can't do your second example yet either. Enum variants
           | are not distinct types. It's a commonly requested feature
           | though so hopefully soon...
        
       | eyelidlessness wrote:
       | The reason enums are useful--and the private keyword until
       | recently with JS adding private fields--are that they're nominal
       | types. You can have...                 enum HTTPMethod {
       | POST = 'post',         // ...       }            enum
       | FenceMaterial {         POST = 'post',         // ...       }
       | 
       | ... and you can be sure 'post' is not ambiguous.
       | 
       | Private fields have the same benefit, which is particularly
       | useful for treating abstract classes as nominal interfaces. But
       | yes, if your target environments support private fields natively,
       | it's more idiomatic to use those than the private keyword now.
       | 
       | I _generally_ avoid namespaces, but they're also sometimes useful
       | eg satisfying weird function signatures expecting a callback with
       | additional properties assigned to it. This is of course uncommon
       | in TypeScript, but fairly common in untyped JavaScript and its
       | corresponding DefinitelyTyped declarations.
        
         | rezonant wrote:
         | Namespaces are most useful for external typings, and in some
         | cases are the only way to correctly model them, sadly
        
       | seniorsassycat wrote:
       | Lots of comments seen to have missed that the article isn't
       | recommending against private fields, only the private keyword.
       | 
       | Typescript and JavaScript have a newer brand # to make fields
       | private. The native feature will have runtime benefits while the
       | private keyword creates fields that's are public at runtime.
       | 
       | I still use the private keyword because constructor shorthand
       | doesn't support brands.                   constructor (private
       | field)         constructor (#field) // error
        
         | ironmagma wrote:
         | > We'd like to maintain feature parity with JavaScript unless
         | there's a compelling reason not to.
         | 
         | And ladies and gentlemen, the generated code...
         | 
         | var __classPrivateFieldGet = (this &&
         | this.__classPrivateFieldGet) || function (receiver, state,
         | kind, f) { if (kind === "a" && !f) throw new TypeError("Private
         | accessor was defined without a getter"); if (typeof state ===
         | "function" ? receiver !== state || !f : !state.has(receiver))
         | throw new TypeError("Cannot read private member from an object
         | whose class did not declare it"); return kind === "m" ? f :
         | kind === "a" ? f.call(receiver) : f ? f.value :
         | state.get(receiver); };
         | 
         | Nothing about this says "native JavaScript" to me. Who cares
         | that a private field is technically public at runtime?
        
           | robpalmer wrote:
           | You're looking at down-levelled code. Babel and esbuild would
           | equally produce similar code. This is not a TypeScript issue.
           | 
           | If you want "native" JS output, use the tsconfig option...
           | "target": "esnext
        
       | shadowgovt wrote:
       | The author misses I think an important point regarding enums:
       | using a union type instead means you can't iterate the list of
       | valid values without restating them in the value domain in a non-
       | DRY fashion. The reason enum generates code is that it is both a
       | type construct and a value construct... That ends up being useful
       | in myriad contexts.
        
       | remorses wrote:
       | I would also suggest to avoid too complex genrics, these tend to
       | make te code unreadable and make the compilation super slow.
       | 
       | Usually generic code tries to make everything type safe but
       | having some portions of your code be dynamic is completely fine
       | imo.
        
       | antihero wrote:
       | I disagree with the point about enums, they can be used to alias
       | otherwise inconsistent long strings with more readable,
       | consistent, succinct ones. For instance, product SKUs. I've had
       | absolutely no problems with them with either past or current
       | tooling (e.g. esbuild).
       | 
       | They can also be used in the more traditional form to represent
       | some arbitrary values.
       | 
       | They shouldn't be overused, though.
        
       | breatheoften wrote:
       | Is anyone still using the class keyword in javascript or
       | typescript? private field syntax doesn't matter in the first
       | place if you don't use class {} anywhere ...
       | 
       | I feel like most of the typescript code i've been in recently
       | looked like it needed 0 more class declarations.
        
         | pjerem wrote:
         | Try using Angular without using the "class" keyword.
         | 
         | (btw, don't try using Angular at all if you are already happy
         | with your career)
        
         | eyelidlessness wrote:
         | I use classes quite a lot. _And_ I'm kind of a FP zealot. I
         | generally treat class as a struct value type, and I also
         | generally go to the trouble of marking every property readonly.
         | They're useful for several reasons:
         | 
         | - Abstract classes are useful for defining nominal interface
         | types
         | 
         | - Classes are a clear signal that a set of methods are designed
         | to interact with related data types
         | 
         | - The prototype chain can be helpful for debugging where POJOs
         | may lose information in the call stack
         | 
         | - JS runtimes can sometimes optimize classes in ways they can't
         | with POJOs
        
         | arcosdev wrote:
         | Totally agree. JS has closures why do we need the private
         | keyword?
        
         | gmiller123456 wrote:
         | I use "class" all the time in Javascript (don't use Typescript
         | at all), why wouldn't you?
        
           | breatheoften wrote:
           | I think the oo features tend to not play as well with many
           | styles of functional programming -- at least the forms of it
           | that work well in typescript ... in typescript I tend to
           | represent data as structurally typed "plain old javascript"
           | objects and my program becomes largely just functions that
           | operate on values and return new values. The idiomatic ways
           | available in the language to copy and produce new values from
           | old values tend to be straightforward and without as many
           | gotchas when the values themselves carry all their meaning
           | without the prototype chain. Once the prototype chain is a
           | important factor in the program behavior you tend to have to
           | keep track of "how exactly was a value with this shape
           | acquired" -- i can't as easily just roundtrip it through some
           | json for example -- and copying a value tends to not be as
           | composable an operation with a tree of objects where the
           | reachable sub objects all have behavior based on prototype
           | chain.
           | 
           | When the prototype chain is involved I think the value gained
           | from structural typing tends to decrease -- or at least
           | exposes the programmer to more sharp edges.
        
             | cageface wrote:
             | Also classes don't serialize nicely so they're a headache
             | when dealing with things like redux actions, api endpoints,
             | storing in local storage or a db etc. POJOs are a lot
             | smoother.
        
       | orta wrote:
       | Yeah, for sure, I agree with all of this. I think the best way to
       | write TypeScript is to really treat it like the tagline
       | "JavaScript With Syntax For Types" - some of the extras
       | TypeScript provided from before TC39 were active have much less
       | relevance now.
        
       | yCombLinks wrote:
       | He is completely incorrect about the purpose of enums. It isn't
       | to simplify changing all locations of an occurrence. It is for
       | defining domain concepts. It is to communicate to other code
       | users, here is every allowed permutation of this type.
        
         | KuhlMensch wrote:
         | I don't know if you are correct, but this is closest to how I
         | think about it
         | 
         | Mechanically, a const dictionary, or a string union might
         | achieve the same.
         | 
         | But semantically, an enum (sometimes) reads better.
        
         | seniorsassycat wrote:
         | You can do that with sum types.                   type methods
         | = 'a' | 'b'         const value: methods = 'c' // error
         | 
         | The difference is enums are also nominal, while most types are
         | structural.
        
       | joshstrange wrote:
       | After trying enums in TS a long time back I also realized it was
       | best to avoid them. Here is my solution to this:
       | // filename: role.ts         export const Role = {
       | CUSTOMER: 'customer',             ADMIN: 'admin',
       | SYSTEM: 'system',             STAFF: 'staff',         } as const;
       | type TRole = keyof typeof Role;         export type TUserRole =
       | typeof Role[TRole];
       | 
       | Using this structure I can reference any of my roles by
       | `Role.CUSTOMER` and the value is `customer` because it's just a
       | `Record<string, string>` at the end of the day. But I am able to
       | type things by using my `TUserRole` so a function can required
       | that the input be one of the values above. For me this is really
       | clean and easy to use. Note the `type TRole` isn't exported as
       | it's only an intermediary in this process, I could just as well
       | name it `type temp` in all my files and never worry about
       | conflicts.
       | 
       | This way I'm not spreading "special" strings all over my code
       | (always a sign of code smell for me), I can changed everything at
       | a central location, and it's valid JS (it's just an object).
       | 
       | EDIT: I know that `as const` seems unnecessary but I'm pretty
       | sure it's needed for some reason, I whittled this down to the
       | smallest/simplest code block I could and I just copy/paste this
       | pattern whenever I need enum-like functionality.
        
         | conaclos wrote:
         | `as const` is needed for ensuring that TypeScript uses literal
         | types for the enum members. Otherwise you ends with `TUserRole
         | = string`.
        
         | cypressious wrote:
         | Have you tried `const enum`?
         | 
         | See
         | https://www.typescriptlang.org/docs/handbook/enums.html#cons...
         | 
         | > Const enums can only use constant enum expressions and unlike
         | regular enums they are completely removed during compilation.
         | Const enum members are inlined at use sites.
        
           | ragnese wrote:
           | I remember reading somewhere that the TypeScript devs
           | considered const enums to be a mistake and recommend against
           | using them. I don't remember why, though.
        
             | russellsprouts wrote:
             | const enums are one of the few cases where type information
             | changes the emitted JS, something that's arguably a bigger
             | problem than TypeScript-specific, but still just syntax
             | sugar, syntax highlighted in the article.
             | 
             | Const enums are erased at compile time. If you have a
             | reference to `MyEnum.VAR`, TS has to check whether the enum
             | is a const enum, and if so, replace with something like `1
             | /* VAR */`. This means that the type information in one
             | file (where the enum is defined) is necessary to determine
             | the proper output of any file that uses it.
        
               | ragnese wrote:
               | Thank you for the answer! I won't waste your time because
               | I'm sure the answer is somewhere on the web, but off the
               | top of my head, I don't understand why TS "has" to emit
               | different JS. I would've assumed that the entire point of
               | a const enum is to inline the raw value in the emitted
               | JS, and thus, the programmer should be careful to
               | remember/know that the code is dealing with raw
               | ints/strings.
        
               | russellsprouts wrote:
               | You potentially get a problem for every '.' expression.
               | In this code:                 import {Something} from
               | './file';       console.log(Something.PROP);
               | 
               | TypeScript doesn't know what to emit without type
               | information. If Something is a class, then the JS will
               | look the same. But if it's a const enum, then TypeScript
               | has to erase the Something.PROP expression and replace it
               | with the constant value of that enum member, since
               | Something will not exist at runtime.
        
           | [deleted]
        
         | conaclos wrote:
         | You can also name your type with the same name as the value:
         | export const Role = {           CUSTOMER: 'customer',
         | ADMIN: 'admin',           SYSTEM: 'system',           STAFF:
         | 'staff',       } as const       export type Role = typeof
         | Role[keyof typeof Role]
        
           | joshstrange wrote:
           | :facepalm: of course I can, I don't know why it never
           | occurred to me. Probably a case of "if it's ain't broke" but
           | going forward I'll probably switch to this style.
        
             | conaclos wrote:
             | For a long time I avoided to use the same name for a type
             | and a value because I was afraid of possible breaking
             | change in the feature.
             | 
             | And then I realized that this is an intended feature of
             | TypeScript: type merging. Here the type `Role` merges with
             | the type of the value `Role :)
        
         | ragnese wrote:
         | I disagree. Enums are fine if they're string-only. It's only
         | the numeric enums that cause the issues everyone complains
         | about enums for. If there were a lint for making sure all enums
         | are string-only, it would be the best solution, IMO.
         | 
         | AFAIK, your solution is (slightly) worse than an enum in
         | several ways and better in none:                   export enum
         | Role {             CUSTOMER = 'customer',             ADMIN =
         | 'admin',             SYSTEM = 'system',             STAFF =
         | 'staff',         }
         | 
         | I _think_ that implements everything yours does, but is easier
         | to grok and fewer lines /declarations.
        
       | sgt wrote:
       | We evaluated TS for a recent project but ended up with JS (and
       | VueJS 3). I have a feeling it would have taken much longer to
       | develop using TS, but lacking experience in TypeScript it's hard
       | to say. I find JS pretty neat for exploratory programming.
        
         | kwinten wrote:
         | I can assure you that doing it in TS would not have been any
         | slower and you would have gained all the benefits that static
         | type checking brings with it.
         | 
         | VueJS 3 in particular is much better suited to use TS than its
         | predecessor (without any additional plugins).
        
           | sgt wrote:
           | Can probably add types later, and consider it once we get the
           | project underway. I am not saying you are wrong, but I have
           | heard TypeScript developers complain about the turnaround in
           | development being slower.
        
             | pjerem wrote:
             | Well, typescript being a superset of JavaScript, you can
             | use TypeScript compiler and just write plain JavaScript.
             | 
             | The only difference is that you'll be warned when you write
             | inconsistent (buggy) code. And that your IDE will
             | autocomplete with only compatible values.
             | 
             | There is absolutely no way typescript slows down
             | development, you're just totally free to ignore any or all
             | of it. But it will help you more than you imagine.
             | 
             | tbf, the only valid reason not to use ts today is if your
             | code targets directly the browser without any build step
             | and is referenced as is by your html.
        
       | _zooted wrote:
       | Article makes no real good arguments about not using the private
       | keyword. It's more descriptive and carries knowledge from other
       | languages compared to putting a hashtag in front of a variable
       | name.
        
         | kwinten wrote:
         | I agree. Just because JS has its way of doing it does not mean
         | you need to stick to that. I mean, TS adds a bunch of new
         | keywords to the language that JS lacks. Why not simply stick to
         | those intuitive keywords rather than this weird variable naming
         | nonsense that JS has to use because it doesn't have access
         | modifiers of any kind?
        
       | 01acheru wrote:
       | > it has to generate new JavaScript code that doesn't exist in
       | the original TypeScript code
       | 
       | That's nonsense, the code is there otherwise what are we talking
       | about?
       | 
       | You just need to understand how some TS features map to JS,
       | that's all.
        
         | chrismorgan wrote:
         | I think you may have misunderstood the complaint, which is
         | perfectly valid. A few paragraphs earlier:
         | 
         | > _The downside to enums comes from how they fit into the
         | TypeScript language. TypeScript is supposed to be JavaScript,
         | but with static type features added. If we remove all of the
         | types from TypeScript code, what 's left should be valid
         | JavaScript code. The formal word used in the TypeScript
         | documentation is "type-level extension": most TypeScript
         | features are type-level extensions to JavaScript, and they
         | don't affect the code's runtime behavior._
         | 
         | And:
         | 
         | > _Most TypeScript features work in this way, following the
         | type-level extension rule. To get JavaScript code, the compiler
         | simply removes the type annotations._
         | 
         | > _Unfortunately, enums break this rule._
         | 
         | And then there is an explanation about why this is important:
         | it makes life hard for tooling, especially _fast_ tooling; for
         | in the absence of such features, JavaScript tooling can support
         | TypeScript with little bother, just dropping the TypeScript
         | bits and getting equivalent JavaScript; but in the presence of
         | such features, they have to either use tsc (slow!) or implement
         | more TypeScript-specific stuff.
         | 
         | Compilation of most TypeScript features to JavaScript simply
         | _removes_ the TypeScript bits. Enums, however, have to be
         | _transformed_ , adding to the output JavaScript something that
         | was not in the source _JavaScript_.
        
           | antihero wrote:
           | esbuild seems to support enums fine
        
             | a_humean wrote:
             | It doesn't support one variant of them, const enums for
             | example. That ties you to tsc emit. Its pretty clear that
             | if the tsc team could they would remove enums and favour
             | literal unions.
        
               | leodriesch wrote:
               | I've just looked this up and it seems to support `const
               | enum` just fine[0]. I remember Babel not being able to
               | process `const enum`, since it goes across module
               | boundaries and Babel does not.
               | 
               | [0]: https://github.com/evanw/esbuild/issues/128
        
       | _under_scores_ wrote:
       | Personally I don't get the argument against enums. From what I
       | can tell it's purely to do with the symantics of what Typescript
       | _is_ , rather than any inherently bad property of enums.
        
       | armchairhacker wrote:
       | tldr: avoid features in TypeScript which must emit runtime code
       | when compiling to JavaScript - that is, any TypeScript-exclusive
       | feature that's not type annotations.
       | 
       | Honestly most of these features are really useful, and as someone
       | who never wants to work in raw JavaScript, I wish they were just
       | added to JavaScript instead. Why shouldn't JavaScript have enums,
       | namespaces, private modifiers which aren't #, and decorators? JS
       | already gets new features which break backward-compatibility,
       | mine as well add features which fix compatibility with
       | TypeScript.
        
         | throw_m239339 wrote:
         | I completely disagree with that article. Instead of "avoid this
         | or that", one should be understanding how Typescript compiler
         | works and not treat it as a blackbox. Ultimately, Typescript
         | does compile to Javascript.
         | 
         | One cannot use Typescript without understanding Javascript,
         | since every single Javascript library is not written in
         | Typescript.
        
           | withinboredom wrote:
           | One should also understand the machine code that a
           | traditional language compiles to and how the CPU executes it.
           | Ultimately, your language compiles to machine code. /s
           | 
           | No one should need or worry about what an abstraction
           | compiles to unless they're specifically working on that
           | abstraction or there's a language bug.
        
             | throw_m239339 wrote:
             | Typescript compiler is first and foremost a type system for
             | Javascript. It's not an independent language. In fact it
             | broke backward compatibility many times to follow the ES
             | spec.
             | 
             | > One should also understand the machine code that a
             | traditional language compiles to and how the CPU executes
             | it. Ultimately, your language compiles to machine code. /s
             | 
             | If libraries you use are all written in machine code, then
             | sure, you should have an understanding of machine code.
             | Your comparison clearly doesn't work here.
             | 
             | > No one should need or worry about what an abstraction
             | compiles to unless they're specifically working on that
             | abstraction or there's a language bug.
             | 
             | When an abstraction is that leaky, it's barely an
             | abstraction. Typescript does force you to choose a
             | Javascript version as a compilation target. Obviously you
             | are forced to know what Javascript version supports what
             | feature because Typescript isn't going to polyfill every
             | missing feature depending on your Ecmascript target.
        
         | austincheney wrote:
         | This is how ES6 got classes. Developers wanted JS to be some
         | other language they favored more. In that case specifically
         | people were really hoping to make the language look and feel
         | like Java, probably because they were trained in Java and
         | couldn't figure out functions as first class citizens.
        
           | mikojan wrote:
           | The Java crowd indeed gave us god awful classes.
           | 
           | But before that there were countless competing models for
           | creating objects or object factories.
           | 
           | Obviously, JavaScript is an object oriented language, too.
           | You cannot escape that fact if you are determined to make the
           | browser paint anything.
           | 
           | Classes effectively solved the "How?"
           | 
           | To pretend you don't need object orientation in JavaScript is
           | really trying hard to make JavaScript into an entirely
           | different language.
        
             | austincheney wrote:
             | > But before that there were countless competing models for
             | creating objects or object factories.
             | 
             | Java and C# had object factories even though in those
             | languages classes could not be avoided. People wanted
             | classes because they could not figure how to program
             | without them.
             | 
             | > To pretend...
             | 
             | Don't use _this_ or _new_ in your code and suddenly a
             | tremendous amount of your code is exposed as unnecessary
             | superfluous vanity. That isn't making the language into
             | something else.
        
               | mikojan wrote:
               | > Java and C# had object factories
               | 
               | Object factories were competing models (plural) for
               | creating any object. A total replacement for classes and
               | the like, not an augmentation.
               | 
               | Here's one such model:                 function
               | createCar(spec) {         const {speed} = spec;
               | let position = 0;                return Object.freeze({
               | move() {            position += speed;           },
               | get position() {             return position;           }
               | });       }
               | 
               | And so you'd find this or any other model or multiple
               | competing models in the very same code base.
               | 
               | It sucked.
               | 
               | > Don't use this or new in your code
               | 
               | You are going to be mutating the internal state of
               | objects. Using this and new or not.
        
           | throw_m239339 wrote:
           | I think class were needed because too many developers were
           | creating their own (incompatible) class systems on top of
           | prototypal inheritance (which is more verbose).
           | 
           | The problem with Typescript is that too many Typescript
           | developers don't understand Javascript itself, which is an
           | completely different issue. That and the obsession for some
           | to reproduce JEE everywhere including in the browser...
        
             | moystard wrote:
             | What do you mean by reproducing JEE? Leveraging OOP in your
             | Javascript programs?
             | 
             | I really dislike this tendency of certain Javascript
             | developers to qualify combining OOP with Javascript as "not
             | understanding the language".
        
               | tentacleuno wrote:
               | In my humble opinion, they're both fine. Use functions
               | where you need to, and do the same with classes. They're
               | both citizens of the language, so why not utilize them?
        
               | moystard wrote:
               | I agree with you. They both serve a purpose and should be
               | used when they make the most sense.
        
           | wdb wrote:
           | I always thought it was because of ActionScript 3
        
             | throw_m239339 wrote:
             | You mean ES4, the revenge ;)
             | 
             | Proxies and classes were definitely salvaged from
             | ES4/ActionScript/Jscript.net.
             | 
             | We wouldn't be needing Typescript, had ES4 been adopted
             | (ironically Microsoft was against it, because
             | Silverlight...).
             | 
             | Someone definitely needs to write a book about the whole
             | saga.
        
         | cromwellian wrote:
         | I'd go further. There's no strong argument for why code-gen is
         | bad. Why is a compilation process that is simply "remove type
         | annotations" inherently better than one that emits code, or
         | does code transformations, other than just personal preference,
         | aesthetics, or simplicity?
         | 
         | About the only positive that is qualitatively different is the
         | simplicity of comparing the output to the input. But for
         | someone building a large typescript codebase, and who uses
         | sourcemaps, it's not really a big issue. There are many many
         | languages that compile-to-JS, and I feel that insisting on
         | 'purity' for purity's sake isn't really a good justification.
         | That, TS as a super-set of JS instead of as a isomorphic
         | mapping between constructs is a perfectly viable way to
         | innovate in the language space.
        
           | armchairhacker wrote:
           | I think TypeScript loosely mapping to JS is important if just
           | because JS sourcemaps _suck_ , like they're so often randomly
           | ignored in callstacks etc. And JS semantics are so subtle it
           | makes transpiling any other language a pain.
           | 
           | But this doesn't justify removing _every_ codegen feature.
           | Namespaces and enums won 't make your code less reasonable,
           | and moreover they're just syntax sugar, they don't change
           | actual JS semantics.
        
           | tuyiown wrote:
           | I see the next to zero codegen in typescript as a strategy:
           | it removes all discutions about languages features besides
           | typing, guarantees next to zero issues in production in case
           | of code generation bug, making compiler deliveries safe and
           | avoid need of coordination in toolchain.
        
             | cromwellian wrote:
             | Enums and namespaces are hardly complex code gen or
             | generate 'issues'. People would often manually namespace in
             | JS a few years ago, and namespaces and enums can really
             | just be seen a lightweight holder object instances. Indeed,
             | enums in Java are class instances.
             | 
             | Besides, there is an straightforward way to remove enums
             | from a program just like removing type annotations: Inline
             | them as static fields of an object.
             | 
             | There is a simple syntactic transformation.
             | e.g. change 'enum' to 'const', add a '=' before the '{'
             | and use ':' instead of '='         const HttpMethod = {
             | Get: 'GET',           Post: 'POST'         };
             | // now this no longer breaks         const method =
             | HttpMethod.Post;
             | 
             | Namespaces can be translated in almost the exactly same
             | way.
        
               | chrismorgan wrote:
               | Trouble with your const there as written is that you
               | don't have a type that's equal to "GET" | "POST".
               | Fortunately, this can be done without repetition or _too_
               | much bother:                 const HttpMethod = {
               | Get: 'GET',           Post: 'POST',       } as const;
               | type HttpMethod = (typeof HttpMethod)[keyof typeof
               | HttpMethod];
               | 
               | Maybe wrap the `{...} as const` in Object.freeze(...) for
               | good measure.
               | 
               | It'd be really nice if they'd improve the ergonomics on
               | this in some way (`type Values<T> = T[keyof T]` would
               | reduce it to `type HttpMethod = Values<typeof
               | HttpMethod>`, which is a start but not enough), to make
               | it a genuine and suitable alternative to enum (minus the
               | other frippery that's generated) and const enum (because
               | it's pure JavaScript, not an extension).
        
       | AbuAssar wrote:
       | You can use const enum and the ts compiler will just replace any
       | enum reference with its value inplace.
        
       | auggierose wrote:
       | Enums are fine, just don't forget to use Object.freeze(enumName)
       | afterwards!!
       | 
       | Apart from that, because Typescript has powerful union types, not
       | using enum is perfectly fine as well, for example instead of:
       | enum Relation { Less = -1, Equal, Greater }
       | Object.freeze(Relation);
       | 
       | you could do instead:                   const Less = -1;
       | type Less = -1;         const Equal = 0;         type Equal = 0;
       | const Greater = 1;         type Greater = 1;              type
       | Relation = Less | Equal | Greater;
       | 
       | Apparently you need the additional "type Less" etc. declarations,
       | I would have thought it should work without.
       | 
       | As for private and #, the biggest disadvantage of # is that it is
       | so slow currently. But that will change hopefully soon when # is
       | not compiled as WeakMaps by TypeScript. I would hope they compile
       | private to # later on, backwards compatibility be damned :-D
        
         | robpalmer wrote:
         | #private fields are not slow when used natively.
         | 
         | It's true the down-levelled code that uses WeakMaps is slower.
         | The decision to downlevel is in the hands of the user and is
         | controlled by the tsconfig "target" option.
         | 
         | The only environment that needs downlevelled #private fields is
         | IE11.
        
       | crabmusket wrote:
       | > We recommend the new #somePrivateField syntax for a
       | straightforward reason: these two features are roughly
       | equivalent.
       | 
       | The author of the article surely knows the significant difference
       | between JS private fields and TS private fields: TS private
       | fields can be easily circumvented, whereas JS private fields
       | cannot. See TS playground link[1]
       | 
       | I think this is a significant enough point that people should
       | _not_ be taught that TS 's private is just a different way of
       | doing the same thing. I've always said that TS is basically a
       | fancy linter, and sometimes that's exactly what you want.
       | 
       | TS's private keyword communicates programmer intent, but lets you
       | do what you like when you really have to. Just like the rest of
       | TS.
       | 
       | [1]:
       | https://www.typescriptlang.org/play?#code/MYGwhgzhAECC0G8BQ1...
        
       | Tyriar wrote:
       | Const enums specifically are great, since they disappear on
       | compile you can use them to internally document strings/numbers
       | with verbose names/comments without the cost of a const variable
       | that would appear after compilation.
       | 
       | As for the fact that types cannot simply be stripped out, I've
       | found building using plain tsc and have the bundler target tsc's
       | output directory. This separation is needed since most tools
       | don't support TypeScript project references anyway which I find
       | extremely useful for organizing internal modules.
        
       | concerned_user wrote:
       | What is there left to use in the end? Type annotations?
       | 
       | Maybe recommend not to use Typescript altogether then. Their only
       | reasoning for this seems to be that the features "be more likely
       | to break when using build tools other than the official
       | TypeScript compiler".
        
         | activitypea wrote:
         | Their reasoning is missing the forest for the trees. The point
         | is that most TS features do not map easily to JS, making any
         | interaction with the resulting code a pain. That includes any
         | static analysis of the JS output (not all tools support TS,
         | after all), debugging both with step by step and console logs,
         | and monitoring in prod.
         | 
         | > What is there left to use in the end? Type annotations?
         | 
         | I've always used TS for the types and nothing more, and been
         | happy with it. Seems to me that most of the JS community has
         | come to adopt this approach over time, and I don't see what's
         | bad about it.
        
         | chrismorgan wrote:
         | Type annotations are the whole _point_ of TypeScript. The rest
         | is mostly distraction and historical decisions that made sense
         | at the time but have aged poorly.
        
         | Zababa wrote:
         | > What is there left to use in the end? Type annotations?
         | 
         | That's what Typescript mostly is, that's where its success
         | comes from. Type annotations for existing JS code. The vast
         | majority of people need to type existing JS code, and compile
         | to JS. A few minority need some specifics from the TS type
         | system. Outside of that, there are other options.
        
       | smoyer wrote:
       | The arguments against the four language features in this article
       | all boil down to it not being Javascript. If I wanted to write in
       | Javascript, I'd use files with a _.js_ extension. If you 've
       | picked Typescript, I don't think you should worry about whether
       | your code is also valid Javascript "if you remove all the type
       | information" (who's going to do that anyway.)
       | 
       | Unless you're a solo developer, what's more important it to agree
       | on languages and conventions that apply to your team's projects.
       | Once that's done, any change to the team's work process should be
       | a conscious, measured decision.
       | 
       | Note that I also used CoffeeScript 10ish years ago and still
       | consider it to be a superior experience to working in plain-old
       | Javascript.
        
         | rezonant wrote:
         | While I agree with the general sentiment, there's a bit of
         | pedantry for you here: Typescript itself removes all the type
         | information when you compile, it's not about the developer
         | doing that themselves outside the context of the compiler. But
         | that is a moot point with regard to the suitability of the
         | features as the article itself suggests-- TS doesn't break your
         | enums during compilation, a suitable JS representation is
         | emitted.
         | 
         | Occasionally I think I might want to use enums in a place but
         | then find that other solutions like const collection objects or
         | plain constants works fine enough for the purpose at hand. I
         | don't think it's a problem if folks use enums though, it's
         | really not a big deal.
        
         | mhoad wrote:
         | I continue to maintain that despite all the nice features from
         | ES6 onwards working in straight JavaScript is psycho shit. It's
         | the NFTs of development.
         | 
         | I'm genuinely looking forward to the day where we have other
         | viable options for the DOM. I see TypeScript at best as a
         | temporary band aid because you're still stuck in the god awful
         | NPM ecosystem at the end of the day.
        
           | rezonant wrote:
           | I disagree with almost all of this, but I will say have you
           | checked out Deno? It is in fact Typescript without the NPM
           | ecosystem (amongst many other interesting aspects)
        
             | mhoad wrote:
             | Honestly, I'm more waiting for WASM garbage collection to
             | officially land as a standard.
             | 
             | It won't get me DOM level access but I can at least move a
             | lot of my code there. I'm also quietly hopeful that canvas
             | based rendering can make some huge improvements in the next
             | few years so it doesn't feel like Flash 2.0 but I'm ready
             | to at least _start_ thinking about letting go of the DOM as
             | the thing I have to care about.
             | 
             | Until then I'm having a good time with Lit (lit.dev) for
             | building web apps that need to be super snappy and "web
             | feeling" which is still basically every customer facing
             | thing.
             | 
             | But in a dream scenario I would way rather be writing apps
             | in Flutter which was at least built from the ground up for
             | building complex user interfaces in a sensible way, but
             | that whole ecosystem is still in some very early days on
             | the web and isn't a good choice right now for most things,
             | hoping that changes in a few years as they also seem to be
             | targeting the WASM + Canvas path and the web as a platform
             | isn't there on that yet and neither are they.
        
               | rezonant wrote:
               | There are a few big problems with using Canvas for UI on
               | the web. First and foremost is accessibility- there is no
               | way for your app to convey the information screen readers
               | are able to get from analyzing the DOM along with the
               | ARIA metadata that you (should) put into your markup.
               | Furthermore, users who have trouble using a mouse can use
               | the keyboard on the web, and it usually works very well
               | since the browser handles it and the browser has been
               | battle tested. You would need to implement your own
               | keyboard handling scheme (though I'm certain a ton of
               | apps just wouldn't bother). What about scrolling with
               | touch input? You would have to implement that too and
               | good luck making it as smooth and performant as the
               | system's own native scrolling (let alone making it use
               | the appropriate rubber banding- iOS and Android have
               | separate ways of doing this because Apple patented the
               | original iOS rubber band scrolling)
               | 
               | Secondly, there is no way for automated agents to extract
               | content from your user interface. This includes search
               | engines, browser extensions, the browser itself, or your
               | end users. I think that goes against what the web is, and
               | I hope other devs agree. The mutability of HTML (and thus
               | the DOM that represents it at runtime) is a strength, not
               | a weakness
        
               | mhoad wrote:
               | This was my first reaction also and I think it's
               | understandable but having looked at proposed solutions to
               | it as well it actually doesn't seem like such a big deal.
               | 
               | I apologise if I get some minor details wrong here as I
               | am doing this on a phone and recalling this from memory
               | because I don't have the time to grab the sources right
               | now.
               | 
               | However... the short version of the plan to solve this
               | that I seem to recall to this:
               | 
               | The Flutter team specifically seemed to indicate that
               | they are already used to operating in non DOM
               | environments where they have to support accessibility
               | across Android, iOS, MacOS, Linux and Windows that doing
               | it on web actually isn't that big a deal as it first
               | seems.
               | 
               | They already have all of the code in place that builds a
               | full tree (like the DOM) which does a complete mapping
               | between every element (widgets in Flutter lingo) on the
               | canvas to their respective bits of accessibility info.
               | They then just take that tree and hook it up to the
               | respective accessibility APIs that each platform exposes.
               | 
               | At no point have they indicated that this looked like it
               | was going to be a serious roadblock or challenge for
               | them. I believe them and they have a history of this
               | approach working elsewhere.
               | 
               | There is just too much money riding on this investment
               | for them not to get accessibility 100% correct in a web
               | native way.
               | 
               | From there if you are able to expose everything through
               | accessibility APIs then you presumably also have
               | everything you need as a search engine or an adblocker to
               | actively modify that canvas.
               | 
               | This is also AFAIK already a solved problem for them in
               | other products where they are using canvas based
               | rendering such as Google Earth and Google Docs.
               | 
               | I don't have the details beyond that right now sorry but
               | that passes the sniff test for me at least.
        
       | maronato wrote:
       | The article's premise is wrong imo. Typescript is a superset[1]
       | of javascript, so typescript-only syntactic sugar is entirely to
       | be expected.
       | 
       | [1]: https://github.com/microsoft/TypeScript (repo description)
        
         | gwbas1c wrote:
         | Not saying I agree, but this is a completely valid viewpoint.
         | 
         | When I initially saw Typescript, I thought the point was to add
         | features from strongly-typed languages and then transpile into
         | Javascript. (IE, a more modern version of GWT, a Java to
         | Javascript transpiler.)
         | 
         | The point of the article, though, is that Typescript works best
         | when its extensions to the language can simply be dropped.
         | That's clearly a "we've worked with this for many years and
         | this is a big lesson from experience" statement, so I wouldn't
         | discount it.
        
           | kodemager wrote:
           | I recently picked up TypeScript and I sort of agree with the
           | author. It's just so much cleaner to use Types instead of
           | enums. That being said, I don't think enums are bad if there
           | is a good reason to use them. Checking if something is a type
           | of X isn't one such thing in my opinion, but that's probably
           | religion.
           | 
           | Namespaces mKe no sense to me. It's probably because
           | Microsoft drives TypeScript, but even though I was a C#
           | developer for 10 years before moving on, they've just always
           | been terrible to me. Their functionality is the sort of thing
           | that is nice in theory, but really terrible in real world
           | projects that run for years with variously skilled developers
           | in a hurry.
           | 
           | Private is silly to me, but this is mostly because classes
           | are silly to me. I can see why you'd want it if you use a lot
           | of classes, I just don't see why you would do that unless
           | you're trying to code C# in TypeScript. One of the things I
           | loved the most about switching from C# to Python was how easy
           | it was to use dictionaries and how powerful they were. The
           | combination of TypeScript interfaces, Types and maps is the
           | same brilliance with type safety. But once again, it's sort
           | of the thing where classes sometimes make sense, and when
           | they do, so might private.
        
           | iakov wrote:
           | Typescript works fine with it's extensions to the language.
           | In fact, I can't imagine how it would not work fine - that
           | would be a serious issue in the official compiler or tools.
           | 
           | It's the other tools that author is/was using that are having
           | issues. It's silly to provide blanket statements about very
           | useful features like enums or namespaces just because some
           | third-party tool is struggling with them IMO.
        
       | donatj wrote:
       | The reasoning behind most of this, that the output JS is not a
       | strict subset of the input is silly and imho represent a
       | misunderstanding of what Typescript is going for. It's a full
       | fledged language that compiles to readable JS, not just
       | _annotated JavaScript_.
       | 
       | There are many features in Typescript where it simply isn't just
       | outputting a subset of the input, and many of them are the best
       | parts of Typescript.
       | 
       | If you just want JavaScript with types, there are other languages
       | that do that, but Typescript offers so much more.
        
         | Zababa wrote:
         | > It's a full fledged language that compiles to readable JS
         | 
         | Compiling to readable JS is not one of TS's goals. For example:
         | 
         | https://www.typescriptlang.org/play?noImplicitAny=false&targ...
        
           | donatj wrote:
           | That seems _very readable_ , especially in comparison to
           | something like Dart or Elm's output, both of which can output
           | thousands of lines from something as simple as your example.
           | 
           | From the language goals
           | 
           | > 4. Emit clean, idiomatic, recognizable JavaScript code.
           | 
           | https://github.com/Microsoft/TypeScript/wiki/TypeScript-
           | Desi...
        
             | mhoad wrote:
             | I don't really see the value proposition of having a
             | compilation step that also prioritises readability when you
             | have source maps.
             | 
             | I love Dart personally and I mostly see it's compile to
             | nonsense looking code as a feature not a bug because it's
             | an ACTUAL compilation step worked on by ex Chrome team
             | members who understand V8 internals not just code splitting
             | and running terser over it and calling it a day. Want to
             | get the same compilation optimisations that Google uses to
             | run all of their multi billion dollar ad business? Cool,
             | that's enabled by default out of the box. [1]
             | 
             | The part where Dart on the web falls over for me is that
             | they have shitty support right now for modern web APIs.
             | They are building against some ancient version of Chrome's
             | WebIDL files so you can totally forget about things like
             | web components for example.
             | 
             | So in that sense it doesn't feel like a sensible choice in
             | 2022 for basic web development which is a shame because
             | it's otherwise probably the best developer experience I've
             | ever seen.
             | 
             | [1] I say this somewhat theoretically, I don't know that
             | Dart is in anyway an obvious thing to point to in terms of
             | web performance from what I had seen casually. I think
             | their goal there you can write huge business critical
             | applications with stupidly large code bases and still get
             | good performance. But nobody's experience after using
             | Google Ads is to talk about how snappy it was.
        
               | Zababa wrote:
               | > I don't really see the value proposition of having a
               | compilation step that also prioritises readability when
               | you have source maps.
               | 
               | It's to increase adoption. Some people still remember
               | migrating to coffeescript and away from it. It's in line
               | with tsc accepting regular JS files, the degrees of
               | strictness, things like that. Typescript is optimized to
               | be adopted by the maximum number of people, which in turn
               | increases its usefulness, the feedback they can get,
               | their influence on JS. People are going to write bindings
               | for popular libraries, even migrate them.
               | 
               | Some other people (like at my job) have some people use
               | typescript, and others the generated code. It makes
               | debugging and reasoning about code easier.
               | 
               | As for Dart, I'm not really convinced. The language seems
               | to have the same philosophy as Go (incremental
               | improvement over old technologies), and while for Go it
               | works because Go is relatively "low level" (lower than
               | Dart), for Dart it's just weird.
        
               | mhoad wrote:
               | If you're going to be working in a multi language code
               | base I am with you. Handing people garbage looking
               | generated or compiled code and saying work with this is
               | going to require a solid set of well defined interfaces
               | at a minimum and maybe like you said even giving up all
               | of that and having to make one thing look like the other.
               | All of what I said was under the assumption that you
               | don't need to think about JavaScript again.
               | 
               | Im making the argument that JavaScript is a target that
               | we have all been collectively forced into due to the
               | limitations of the web as a platform but short to medium
               | term horizon that is changing where things like WASM are
               | maturing and will let a lot of new options flourish (.NET
               | folks seem to be probably leading this charge currently)
               | 
               | But just stopping to think about the implications of that
               | kind of changing landscape and what's coming, I don't
               | think aiming for 100% JS interop not just from a code
               | perspective but it also the entire tooling and developer
               | ecosystem perspective is going to be as important.
               | 
               | Again, I just think people are somewhat forced to at the
               | moment because the web has always been a one language
               | show. That wasn't because JS was the best choice but
               | rather a limitation of the platform itself which is
               | already in the early stages of changing.
               | 
               | For Dart specifically I kind of get what you're talking
               | about I guess because it's pretty commonly referred to as
               | the best bits of JS and Java put together while ditching
               | the worst parts of each so it's clearly aimed at
               | productivity for application sized code bases rather than
               | something low level but again... that's literally why
               | they have a proper complication step because getting it
               | down to something a lot more low level is exactly what a
               | compiler is for. That doesn't feel weird to me at all,
               | that actually feels like an incredibly sensible choice.
        
         | Vinnl wrote:
         | > imho represent a misunderstanding of what Typescript is going
         | for.
         | 
         | It's pretty what TypeScript's going for though:
         | 
         | > Avoid adding expression-level syntax.
         | 
         | https://github.com/Microsoft/TypeScript/wiki/TypeScript-Desi...
        
           | donatj wrote:
           | I'm curious what they mean by "expression-level syntax"
           | specifically, because they certainly added a lot of _syntax_
           | that isn't ECMA and aren't just Babel, backporting future
           | ECMA features.
        
       | conaclos wrote:
       | Replacing TS enum with JS objects is really tedious:
       | type HttpMethod = keyof typeof HttpMethod            const
       | HttpMethod = {           GET: "GET",           POST: "POST",
       | } as const
       | 
       | It is even worse with int enum:                 type HttpMethod =
       | Extract<keyof typeof HttpMethod, number>            const
       | HttpMethod = {           GET: 0,           POST: 1,           0:
       | "GET",           1: "POST",       } as const
       | 
       | My personal "avoid"-rules are:
       | 
       | - Avoid enum with negative integers because it generates code
       | that cannot be optimized by the JS engine
       | 
       | - Prefer ESM modules to namespaces, because the first can be
       | tree-shaked.
        
         | joshstrange wrote:
         | I've been using the method I outlined here [0] and it's not
         | tedious to work with at all. That said you might have a
         | different usecase where my example doesn't work.
         | 
         | [0] https://news.ycombinator.com/item?id=30010017
        
       | Tade0 wrote:
       | I thought this is going to be about dependent types or the
       | `infer` keyword(or any other badly documented feature like the
       | latter).
       | 
       | There are features of the type system you should definitely avoid
       | unless you're writing a library. Enums aren't it.
        
       | jacobr wrote:
       | The TypeScript documentation even states "In modern TypeScript,
       | you may not need an enum when an object with as const could
       | suffice ... The biggest argument in favour of this format over
       | TypeScript's enum is that it keeps your codebase aligned with the
       | state of JavaScript, and when/if enums are added to JavaScript
       | then you can move to the additional syntax"
       | https://www.typescriptlang.org/docs/handbook/enums.html#obje...
        
       | ameliaquining wrote:
       | This whole thing feels basically grounded in purity over
       | practicality. In general it's a good idea to write idiomatic
       | TypeScript. Even when I agree with the given recommendations, the
       | given reasons don't seem like the strongest ones.
       | 
       | I most strongly disagree with the recommendation against enums.
       | Realistically, you will probably never run into a compiler bug
       | from enum emit; maybe something like this might happen with a
       | very complicated runtime feature, but enum emit is dead-simple
       | and hard to get wrong (at least if your toolchain has any tests
       | at all, which it presumably should). And they're generally
       | convenient and fill a useful niche, especially numeric enums with
       | implicitly assigned values. (I'm also curious what the article's
       | authors think of const enums.)
       | 
       | Namespaces have been soft-deprecated, modules are pretty much
       | just better, and so I quite agree that you shouldn't use them,
       | though I'm not sure the risk of compiler bugs is the most
       | compelling argument against. (It is more compelling than with
       | enums, since the required code transformations are much less
       | trivial.)
       | 
       | Decorators, especially with metadata, facilitate lots of useful
       | things that otherwise just aren't possible in TypeScript. It's
       | also the case (though the authors seem unaware of this) that they
       | will _never_ be standardized in the current form that TypeScript
       | has them, because they were based on an earlier version of the
       | design that has since been pretty explicitly rejected. The risk
       | isn 't that decorators are never standardized; if that happens
       | then TypeScript will just keep the current design forever and
       | things will be mostly fine. The risk is that they get
       | standardized in an _incompatible form_ and then you have an
       | interesting migration ahead of you. TC39 won 't do this lightly,
       | but no one knows exactly what the future holds. So it is a
       | tradeoff to think carefully about, though in the end reasonable
       | people will disagree.
       | 
       | # vs. private is mostly a matter of style/taste, with two
       | exceptions. First, if you have any code on your page that you
       | don't trust not to be doing weird things, strongly consider #,
       | since it provides protection against corruption of internal
       | state. Second, if you have to support older browsers that don't
       | support #, then don't use it; the compiler can downlevel it, but
       | only at significant code-size and performance cost that you don't
       | want to pay (and debugging it in the browser will also be
       | annoying).
       | 
       | Do the authors also disfavor parameter properties? Those also
       | require emit beyond just stripping types, but are super-
       | convenient and useful and don't really conceptually complicate
       | things.
       | 
       | Incidentally, the feature at the top of my own list of
       | "TypeScript features to avoid" (other than namespaces and other
       | soft-deprecated features) is something entirely different:
       | conditional types. Most other advanced type-system features
       | behave reasonably predictably most of the time, but conditional
       | types are very demanding of maintainers' knowledge of fiddly
       | type-system details. I'm not saying it's never worth it (and in
       | particular the built-in utility types are usually fine even
       | though they're conditional under the hood), but whenever possible
       | I try to reach for something else.
        
         | activitypea wrote:
         | > you will probably never run into a compiler bug from enum
         | emit;
         | 
         | That's true, but diagnosing other bugs is an absolute pain in
         | the butt when your enum value at runtime is 0, 1, or 2. You get
         | all of the readability of C with none of the performance :)
        
           | ameliaquining wrote:
           | I personally haven't found that to be much of a hindrance to
           | debugging, but I suppose this is somewhat a matter of
           | personal preference.
        
         | deckard1 wrote:
         | > good idea to write idiomatic TypeScript
         | 
         | Is there really such a thing? Everyone seems to be writing TS
         | with the "fake it until you make it" mantra, never quite
         | reaching the "make it" phase. People still use "interface" and
         | "type" interchangeably without rhyme or reason. Or "import" vs
         | "import type". No one knows what they are doing in TS. Or why.
         | Just look at this entire comment section.
        
         | heisenbit wrote:
         | > Decorators, especially with metadata, facilitate lots of
         | useful things
         | 
         | Angular 2 would look very different without it.
        
       ___________________________________________________________________
       (page generated 2022-01-20 23:01 UTC)