Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I love this. It reminds me a lot of the work I did in Prism to port what I learned using Haskell in TypeScript: https://dev.to/vncz/forewords-and-domain-model-1p13

Reading the article, I do have some suggestion:

1. Never use isLeft or isRight; they break the composability of the monad instance — use `fold` instead, as late as possible

2. Do not use Enum in TypeScript — they have a runtime representation in JavaScript that's likely messing up a lot of things. Use string unions to accomplish the same result



I really wish the TS team would have used frozen null prototype objects for enums instead. Feels like it would have been a perfect fit.

With that said I am curious why you think one shouldn’t use enums. I know it’s a “factory” IIFE like namespaces at runtime but otherwise I don’t understand why it’s undesirable?


My main issue with Enums in Typescript:

1. Because they have a runtime representation, I cannot do `import type` — and I am importing the entire file (which might have a side effect). While it's still not justified (ideally importing should not do anything) the reality in JavaScript is different.

2. I can swap the enum value with a number or a string and it would still work, even if invalid. See https://www.typescriptlang.org/play?#code/KYOwrgtgBAggxgFwJY... for an example

3. On the other hand, I cannot use strings to create an Enum (https://www.typescriptlang.org/play?#code/KYOwrgtgBAygLgJwJY...) — this is the exact opposite of n. 2

4. Duplicates are possible: https://www.typescriptlang.org/play?#code/KYOwrgtgBAIghgTwPI...


1. You can define enum like `const enum A {...}` and they will not have runtime representation.


Correct, but that does not solve the other issues I pointed.

Additionally, const enums DO NOT have an associated type. That is a problem in libraries, since users cannot reuse the type definition around (it back ports to a string or a number)


const enum doesn't work when in transpileOnly mode (e.g. webpack, Parcel), and Babel treats "const enum" the same as "enum". I'd really love a non-broken const enum in TypeScript, though.


I'd love to see them out of the language. I do not have evidence/document supporting this, but I think it was ultimately a failed attempt of copying the C# feature ignoring the fact that JavaScript works differently.


Why is #3 a problem? It's a feature! Enums are a great way to enforce values to a predefined set. I don't want you to work around it with random strings. Use the enum. That's what it is there for.


The biggest issue I have with enums is that unlikely virtually everything else in TypeScript, they are a "thing" (you correctly mentioned in your next comment that they don't have to be depending on their declaration, but that nuance will be lost on 99% of users). They're not just "types on top of JS", which defeats a lot of expectations in TypeScript. They're also not standard JS, so best practices with them aren't nearly as studied.

If they were to be part of the standard, it would be another story.


Yes. There are so few TS features that are non-type-level and non-standard. One option is to lint against these few features and call it a day.

  {
    "rules": {
      "no-restricted-syntax": [
        "error",
        {
          "selector": "TSEnumDeclaration",
          "message": "Enum restricted."
        },
        {
          "selector": "ClassDeclaration[abstract=true]",
          "message": "Abstract class restricted."
        },
        {
          "selector": "TSParameterProperty",
          "message": "Parameter property restricted."
        }
      ]
    }
  }
(TS decorators are already marked experimental)


There are also const enums, which get inlined/erased at compile time. Even so, I still avoid using enums and prefer literal types instead.


I don’t understand why so many people advise against TS enums. The string variant is no different at runtime from a const of the same mapping, but they also achieve nominal typing in an otherwise structural type system, which is super useful for preventing ambiguity errors


I've done TypeScript for 5-6 years, in up to 100+k loc codebases, and never had a problem with enums, both with tsc and babel (so yeah, `const enum` no worky).

`Object.values(SomeEnum)` is pretty handy, something you can't do with string union types.


See my response above!


1.

Ah thanks for the suggestions. These reminds me scrounging this article https://dev.to/gcanti/getting-started-with-fp-ts-either-vs-v...

My pet peeve doing this in JS/TS is the fact that writing

``` fold( () => someIdealVal, () => someNonIdealVal )(someAssertion(someValue)); ```

means that those functions passed as the parameters might be instantiated at the execution time rather than on compile time. (This is not proven as the final resulting machine code depends on how good the bundler(webpack)/compiler(tsc)/runtime(v8/rhino) is)

For the sake of performance, I am tempted to write it like this

``` const a = () => someIdealVal const b = () => someNonIdealVal

const someFold = fold(a, b); ```

Writing it like this feels worse for readability. The "story of the function" cannot be told top to bottom, therefore other devs who read this must jump between lines non-sequentially like watching Christopher Nolan's memento.

Rather than pitting readability against performance, I prefer to shelve that fold/pipe/chain pattern for later consideration

2.

I can relate to this. Enums are frustrating especially if you have to write library with it.

I have a pretty good time using this alternative instead. https://medium.com/@martin_hotell/10-typescript-pro-tips-pat... this article point number 4


If I'm understanding you correctly, then fp-ts has the `constant` function to enable what you're describing in #1:

    fold(constant('my static fallback'), f)(x)
What sucks about this, as much as it's my preferred pattern, is that without native pattern matching in the language it's unclear which callbacks are for which type constructors. It's not so bad for something like Either, but if you're using something like DatumEither with ~five constructors it's a bit of a mess.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: