Hacker News new | ask | show | jobs
by cromwellian 1608 days ago
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.

2 comments

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.

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.
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.
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).