[HN Gopher] TypeScript is surprisingly ok for compilers
       ___________________________________________________________________
        
       TypeScript is surprisingly ok for compilers
        
       Author : Fudgel
       Score  : 264 points
       Date   : 2023-08-18 05:58 UTC (17 hours ago)
        
 (HTM) web link (matklad.github.io)
 (TXT) w3m dump (matklad.github.io)
        
       | hardware2win wrote:
       | Ive been writing compiler in C# and I dont see anything fancy
       | here except union type
       | 
       | Ive personally decided to avoid visitor pattern bloat and Im
       | waiting for closed enum feature in order to have compile time
       | exhaustive check
        
         | AgentME wrote:
         | I find myself really missing union types when I'm in a language
         | that doesn't have either it or Rust-style enums. The usual
         | alternatives are awkward: either one class representing the
         | union type containing N nullable properties with the documented
         | condition that only one property is ever expected to be non-
         | null, a condition the type system isn't able to check or make
         | use of, or multiple classes extending a common class, which
         | feels really heavyweight. Both solutions require some
         | duplication or clever composition if you want several different
         | overlapping union types.
        
       | andromaton wrote:
       | Can I humbly add "Anders Hejlsberg" to the list of reasons why
       | none of the commenters seem surprised?
        
       | tomp wrote:
       | As the post nicely demonstrates, TypeScript is definitely _not_
       | OK for compilers (and not surprising at all!)
       | 
       | It doesn't even have destructuring pattern matching!
       | 
       | At this point, even Java is better [1].
       | 
       | [1]
       | https://github.com/tomprimozic/caya/blob/master/src/caya/Int...
        
         | dom96 wrote:
         | Pattern matching is definitely not a requirement for a language
         | to be good for writing compilers in.
        
           | [deleted]
        
         | [deleted]
        
         | vlakreeh wrote:
         | With how powerful the type system is you can implement pattern
         | matching via a library pretty convincingly,
         | https://github.com/gvergnaud/ts-pattern is definitely the go-
         | to. That being said pattern matching is hardly a requirement
         | for being ok for implementing compilers.
        
       | domlebo70 wrote:
       | To OP: You could avoid the visitor by using an IIFE style switch
       | using the run utility fn:                   export const run =
       | <T>(f: () => T): T => f();
       | 
       | Now you can go:                   const inferred_type = run(() =>
       | {           switch(blah) {             ...           }         })
        
         | mgreenw wrote:
         | Exactly! I wrote a post about this pattern:
         | https://maxgreenwald.me/blog/do-more-with-run
        
           | domlebo70 wrote:
           | Yeah this is where I heard about it :P I showed all my dev
           | friends, and our minds were equally blown away by the
           | obviousness of it.
        
         | gavinray wrote:
         | You don't even need to do this much, you can just invoke the
         | function and let the return type be inferred.
         | const inferred = (() => "Hello")() // Inferred as "string"
         | 
         | If you really don't want to use an IIFE because you don't like
         | the "()" at the end, you can just:                 const myFn =
         | () => { switch(...) }       const inferred = myFn()
        
       | harha_ wrote:
       | I'm sick of seeing overly complicated TypeScript code. Mainly
       | overly complicated types.
        
         | LeanderK wrote:
         | compilers are really complicated pieces of software. There's a
         | lot of complexity even in small routines. I would expect to
         | encounter complicated types in there, they capture (while
         | complex) intent and meaning.
         | 
         | It's not the frontend glue-code kind of code. The inherently
         | hard problem makes is fun for people who like to solve small
         | puzzles.
        
         | [deleted]
        
       | newusertoday wrote:
       | it isn't, i have codebase in golang that is much larger than
       | typescript and it is pleasant to work with lsp and compiles
       | smoothly. With typescript i have to turn off lsp and even after
       | that it takes long time too compile. There is a reason why people
       | are writing typescript compiler in rust.
        
         | IshKebab wrote:
         | He's talking about compilers for small languages. The
         | Typescript LSP works fine on very big projects like VSCode so I
         | think you'd need an enormous language like C++ or Rust before
         | you'd run into those limits.
         | 
         | But still, I think I'd rather use Rust. I'm pretty sure the
         | code would be nicer (e.g. no need for the explicit tag field or
         | for the visitor hack).
        
           | tejinderss wrote:
           | The author is no stranger to rust (he's creator of rust-
           | analyser). The reason why he's pitching typescript here is
           | due to its high level nature and doesnt have to deal with
           | memory management, low level integer types etc.
        
             | IshKebab wrote:
             | I know. I'm just expressing my opinion that he hasn't
             | really made me want to use Typescript over Rust for this
             | case.
             | 
             | I like Typescript and Deno. But I'd still rather use Rust
             | here.
             | 
             | I would love it someone made a RustScript though which
             | would be basically Rust but with arbitrary precision
             | integers, garbage collection etc. (but still value
             | semantics).
        
           | baq wrote:
           | I see 'TS server has been restarted 5 times in 5 minutes'
           | daily.
        
             | iends wrote:
             | I don't see this and I work on large enterprise websocket
             | server with almost a million daily active users. In fact, I
             | think we had 100% up time for the last 12 months except for
             | a few AWS outages. Codebase is 10 years old and was
             | CoffeeScript -> ES6 -> TypeScript.
        
               | baq wrote:
               | nono, I mean the LSP in vscode. prod deployment is
               | unrelated.
        
         | [deleted]
        
       | andrewcobby wrote:
       | As some how is writing a compilter in TS, I agree it's not too
       | bad. I started with Deno like the author but ended up switching
       | to Bun which, despite some rough edges, I'm enjoying more than
       | Deno and it's _very_ quick! (My main niggle with Bun is the test
       | reporting, but it's getting the job done)
       | 
       | For standard parser generator frontend, Ohm-js[1] is quite
       | pleasant. I wouldn't recommend anyone reviews the offical tsc
       | compiler, it's huuuge - instead there's mini-typescript[2] (in
       | particular the centi-typescript branch[3]) which does a nice job
       | illustrating how tsc is working.
       | 
       | [1] https://ohmjs.org/ [2] https://github.com/sandersn/mini-
       | typescript/ [3] https://github.com/sandersn/mini-
       | typescript/tree/centi-types...
       | 
       | Looking forward to GC an DOM access in WASM.
        
       | lmm wrote:
       | Is that really suprising? Typescript is yet another language that
       | has, kicking and screaming, picked up most of the ML featureset.
       | I'd expect it to be, well, fine; the lack of real pattern
       | matching is a pain, so it's going to be inferior to OCaml, but
       | fine, no different from using C# or Swift or Dart or Kotlin or
       | something of that ilk.
        
         | madeofpalk wrote:
         | Lack of pattern matching is annoying, but Typescript's tagged
         | unions and type narrowing from if-statements are a good
         | substitude and make for a very pleasent time traversing ASTs.
         | C# has it's own way to express similar data structures (with
         | subclasses and interfaces), but I find unions+narrowing much
         | more natural in Typescript.
        
         | DrScientist wrote:
         | Dart pattern matching?
         | 
         | https://medium.com/dartlang/dart-3-1-a-retrospective-on-func...
        
         | IshKebab wrote:
         | > picked up most of the ML featureset
         | 
         | It really hasn't. In this very post he had to use a visitor to
         | work around the fact that switch isn't an expression.
         | 
         | JavaScript's support for iterators is also weirdly shit. It has
         | `.map()` but that only works on arrays. You can't map an
         | iterator!
        
           | bakkoting wrote:
           | You can now! The iterator helpers proposal is stage 3 and
           | shipping in Chrome.                 (new Set([0, 1,
           | 2])).values().map(x => x + 1).toArray()
           | 
           | You can also reduce, filter, find, etc.
        
           | andyferris wrote:
           | I think there's an iterator proposal out there for JS with
           | generic map, filter, etc.
        
         | jillesvangurp wrote:
         | I've done a bit of typescript and kotlin-js. It always strikes
         | me how close those two languages are. Yes there are lots of
         | differences but they aren't that different and you can
         | transition from one to the other pretty easily. I have my
         | preferences (kotlin) but I can work with both.
         | 
         | IMHO typescript could just cut loose from its javascript
         | compatibility. Why not compile it to wasm instead of
         | transpiling it to javascript? Kotlin is in the process of
         | adding a wasm compiler and they already have a js transpiler.
         | The same code results in a lot smaller binaries and loads a lot
         | faster in wasm form. Browser Javascript is not a great
         | compilation target. And who cares what the minified crap that
         | loads in the browser looks like? The only reason for backwards
         | compatibility is allowing javascript projects to easily
         | transition to typescript. But that's increasingly less relevant
         | now that a lot of projects start out being typescript from day
         | 1.
         | 
         | Of course, Assembly script is already a thing. But why not make
         | that a bit more official? It wouldn't surprise me to see people
         | doing a lot of web development in languages like that in a few
         | years.
        
           | jbreckmckye wrote:
           | > IMHO typescript could just cut loose from its javascript
           | compatibility. Why not compile it to wasm instead of
           | transpiling it to javascript?
           | 
           | My fantasy for the past year has been, if I could magically
           | program anything, bringing a compiler and spec wholesale into
           | the world out of the void, I would create a new language
           | (call it WebScript as a placeholder) that
           | 
           | - featured an ML style type system, ADTs, a type syntax
           | nearly indistinguishable from TypeScript
           | 
           | - whose actual core language essentially resembled Kotlin or
           | "Go with exceptions"
           | 
           | - compiled to WASM or JS as a compatibility bridge
           | 
           | Nothing radical. Nothing revolutionary. Just these things
           | would be an immediate plus.
           | 
           | But maybe AssemblyScript is a good enough step towards that.
        
             | srhtftw wrote:
             | I've been hoping to see alternative targets to JS for TS as
             | well. WASM makes a lot of sense but TypeScriptToLua1 also
             | looks interesting.
             | 
             | 1: https://typescripttolua.github.io
        
             | jitl wrote:
             | Go has exceptions: you can use `panic` to throw/catch just
             | fine. The community will bring out the torch and pitchforks
             | because it's "not idiomatic" but if you're programming solo
             | or with other pragmatists don't let it stop you.
        
               | skybrian wrote:
               | Does the community care if it's not in a public API? If
               | it's caught within the same library it doesn't affect
               | anyone else.
        
               | madeofpalk wrote:
               | Or just pass pointers because you need optionality, and
               | get null pointer exceptions for free :)
        
             | andrewcobby wrote:
             | I'm actually working on a language like this. I quite liked
             | ReScript/ReasonML, but having to manually write binding to
             | use TypeScript or JS code is a drag. I'm making a
             | functional language that looks and feels like TS and lets
             | you import TS directly. Mostly just stripping imperative
             | statements, removing class declaration and adding pattern
             | matching and better data constructors (never liked the
             | discriminated unions). WASM as a target is a bit further
             | off.
        
           | Kyro38 wrote:
           | > IMHO typescript could just cut loose from its javascript
           | compatibility. Why not compile it to wasm instead of
           | transpiling it to javascript
           | 
           | In browsers the Wasm runtime doesn't have access to the DOM
           | APIs. So that's wishful thinking ATM.
        
           | AgentME wrote:
           | Typescript is defined to run exactly as the equivalent
           | Javascript you get when all the type declarations are
           | stripped out of the source. There's no benefit in compiling
           | it to WASM unless you had a WASM JS engine (or JS->WASM
           | converter) that executed the JS faster than the browser's
           | built-in JS engine (or created WASM binaries that were
           | smaller than compressed minified JS, which seems extra
           | unlikely when you'd have to include a JS runtime). If that
           | existed for general JS, browsers would just upgrade to use
           | that internally on all JS.
           | 
           | Well, you could in theory get benefits by having
           | optimizations based on the type declarations. This could be
           | awkward to do when Typescript allows zero-runtime-cost
           | interop with untyped JS anywhere and allows any value to be
           | cast through the `any` type and then to any other type. If
           | Typescript with these optimizations is still intended to
           | execute in the same way as its untyped JS equivalent, then
           | the optimizations all need to handle being opted out of when
           | an unexpected value is received, in the same way optimizing
           | runtime-type-tracking JS engines have to be able to abort out
           | of their type-based optimizations. This optimization would be
           | equivalent to pre-warming a JS engine's knowledge of runtime
           | type information, which is an optimization that could be done
           | for JS in general rather than by doing it just in the
           | Typescript project through compiling to WASM.
        
             | hibbelig wrote:
             | Your parent suggested to make changes to TS so that TS is
             | no longer compatible with JS (in the sense you described).
             | Once that happens, compiling to WASM instead of transpiling
             | to JS is a very valid design choice.
        
               | wiseowise wrote:
               | The whole purpose of TS is better JS.
        
             | [deleted]
        
           | orra wrote:
           | I'm afraid that's wishful thinking. JS compatibility is core
           | to the TypeScript ethos, regardless of TypeScript's own
           | popularity.
           | 
           | Besides, enums (?) aside, and ignoring typechecking,
           | compiling TS is really easy right now. Switching to WASM is a
           | high ask.
        
             | horsawlarway wrote:
             | JS compatibility is core to Typescript's own popularity.
        
           | [deleted]
        
           | flohofwoe wrote:
           | "Modern" Javascript essentially _is_ Typescript without the
           | type hints (e.g. if you ignore the historical baggage, JS is
           | actually a fairly decent language).
        
             | usrusr wrote:
             | Yeah, having just jumped into ts after a long js hiatus
             | since back when The Good Parts was still surprising, it's
             | quite awesome to see how much of what I assumed to be "ts
             | stuff" is actually just postdeluvian js. Makes ts more
             | attractive, not less. I do wonder however of it was
             | possible to identify parts of that language superset that
             | are _fully_ redundant (as in not even required for exotic
             | edge cases) and let loose some draconian linter?
        
               | jakubmazanec wrote:
               | You already can kinda do that using ESLint and rules like
               | https://eslint.org/docs/latest/rules/no-restricted-
               | syntax, https://eslint.org/docs/latest/rules/no-
               | restricted-propertie... or
               | https://eslint.org/docs/latest/rules/no-restricted-
               | imports.
        
               | usrusr wrote:
               | Sure, but that's still seems quite mix and match,
               | anything goes, choose your own adventure. There are many
               | positives about this approach, but as a learner a little
               | more common ground between codebases would certainly be
               | appreciated.
        
             | junon wrote:
             | I actually argue JS was a better language before all of the
             | changes made, starting with const/let. The only thing I'd
             | say makes sense are classes but the fact they aren't syntax
             | sugar over prototypes was a mistake.
             | 
             | People wanted a different language, they should have gotten
             | more scripting languages in the browser. Not changing
             | JavaScript so much that it's no longer JavaScript.
        
               | baq wrote:
               | you can still write the old javascript. it'll feel very
               | lonely, though.
        
               | shrimpx wrote:
               | > aren't syntax sugar
               | 
               | They aren't syntactic sugar? Pretty sure `class Foo {
               | blah() {} }` is equivalent to `function Foo() {};
               | Foo.prototype.blah = function() {}`.
        
               | junon wrote:
               | They are not, no.
        
               | shrimpx wrote:
               | My example is incorrect because it doesn't cover all the
               | edge cases but you can desugar es6 to es5, except in es5
               | you can't subclass builtins.
        
               | zdragnar wrote:
               | There are a few very specific differences. In a subclass,
               | 'this' doesn't exist until you call "super" for example,
               | and the constructor will throw an error of invoked
               | without the "new" keyword. [O]
               | 
               | These differences let you extend built-in things that
               | simply can't be done with old prototype constructor
               | syntax. [1]
               | 
               | [O] I should probably be using a more recent reference
               | like MDN, but the 2ality series always sticks out in my
               | mind whenever "just syntax sugar" comes up:
               | https://2ality.com/2015/02/es6-classes-final.html#safety-
               | che...
               | 
               | [1] https://2ality.com/2015/02/es6-classes-
               | final.html#subclassin...
        
               | shrimpx wrote:
               | Thanks. It sounds like subclassing built-ins is where
               | transpilers hit a hard wall. Other syntactic features of
               | es6 can be transpiled.
        
               | bornfreddy wrote:
               | > the fact they aren't syntax sugar over prototypes was a
               | mistake
               | 
               | Totally different meaning.
        
               | shrimpx wrote:
               | What else would 'syntax sugar over prototypes' mean?
        
               | cxr wrote:
               | The result of `let x = Foo()` when Foo is defined as a
               | function is whatever Foo's return value is. Trying `let y
               | = Foo()` when Foo is defined as a class throws a
               | TypeError.
        
               | flohofwoe wrote:
               | Coming into web development fairly recently (after 2013
               | or so) from the statically typed hemisphere and without
               | closely following the Javascript history from the start I
               | naturally cannot agree ;)
               | 
               | (things like the scoping rules for 'var' is just bizarre,
               | same with the concept of 'prototypes')
               | 
               | Looking at typical "old-school" Javascript code gives me
               | the creeps the same way as looking at K&R C code ;)
        
         | moonchrome wrote:
         | That's some very high level view - in reality even tough those
         | languages are in similar categories the experience would be
         | vastly different :
         | 
         | TypeScript - powerful type system but shit underlying stdlib
         | and language (no pattern matching/switch expressions)
         | 
         | Dart - worse than TS because the object model is closed - so no
         | dynamic freedom, but the type system and expressions are weaker
         | then the rest. Also 0 meta programming facilities - Java level
         | of boilerplate and code generators
         | 
         | C# - closest to ML featureset out of the mentioned, but unlike
         | TS doesn't have sum types which will make a lot of things more
         | tedious.
        
           | crooked-v wrote:
           | > no pattern matching/switch expressions
           | 
           | They're still waiting on the do expression proposal for that
           | (https://github.com/tc39/proposal-do-expressions), which has
           | been in the bikeshedding stage for the past five years.
        
             | yunohn wrote:
             | I'm not clear on the benefits of this proposal over just
             | using anonymous functions. Some of the examples just seem
             | contrived.
             | 
             | And the React example makes no sense, you can use a ternary
             | and it is even shorter.
             | 
             | ``` return ( <nav> <Home /> {loggedIn ? <LogoutButton /> :
             | <LoginButton /> } </nav> ) ```
        
               | baq wrote:
               | for me the point is to be able to return from inside
               | multiple expressions from the correct scope i.e. the root
               | function without throwing.
        
               | doctormanhatten wrote:
               | Having written quite a lot of Reason react (similar in
               | syntax to the proposed React example) - while in this
               | particular case because the example is so simple the
               | ternary looks nicer, its also nice to just have a longer
               | expression block in the middle of your JSX when you're
               | doing more complex things.
        
               | graftak wrote:
               | What if you want a `switch` instead?
        
             | graftak wrote:
             | The proposal for pattern matching syntax seems more akin to
             | what they're looking for.
             | 
             | https://github.com/tc39/proposal-pattern-matching
        
           | munificent wrote:
           | Your understanding of Dart sounds a little outdated. We have
           | a fully sound static type system and the language is pretty
           | expressive, especially with the new pattern matching stuff in
           | 3.0:
           | 
           | https://medium.com/dartlang/dart-3-1-a-retrospective-on-
           | func...
        
           | pja wrote:
           | If you're going to use C# for a compiler, why not go the
           | whole hog & use F#?
        
             | delta_p_delta_x wrote:
             | I was going to mention F#. The parent and grandparent
             | comments mention C# and ML, and the intersection of the two
             | is... F#.
        
             | moonchrome wrote:
             | No argument from me, just saying lumping in all those
             | languages together sounds reasonable in theory, in practice
             | they are very far apart.
             | 
             | You'll probably have similar overlap between C# and F#
             | implementations as you would with say TypeScript - they are
             | just that different in practice IMO.
        
           | jezzamon wrote:
           | Could you speak more to the dart, and specifically the "Java
           | level of boilerplate and code generators"?
           | 
           | I've used it a bit but I haven't found it very boilerplatey
           | in general, so I'm interested in learning what contexts you
           | run into that.
           | 
           | I'm assuming you're using it with flutter?
        
         | thebears5454 wrote:
         | Naively, I would have guessed it being harder than C#
        
         | baq wrote:
         | The biggest expressivity pain point in practice for me is
         | try/catch not being an expression, so I can't do
         | const c = try { ... }
        
           | not_alexb wrote:
           | I use iife for stuff like this.
           | 
           | const c = (                   ()=> {             try: {...}
           | }
           | 
           | )()
        
           | smcl wrote:
           | Would you not just move that `try { ... }` block into a
           | separate function or method?
        
           | austin-cheney wrote:
           | I never ever use try/catch in my code. The only time its
           | really necessary is to wrap JSON.parse on use of untrusted
           | input. For everything else it just feels sloppy as through
           | there is insufficient logic in place for handling primary
           | conditions versus edge cases. Also, try/catch will never
           | compile in the JIT.
        
             | afavour wrote:
             | > Also, try/catch will never compile in the JIT
             | 
             | Changing the way you program to fit what a JS compiler does
             | or doesn't do is a fool's errand, IMO. The performance
             | benefits are likely to be minimal, confusion for anyone
             | else who has to touch the codebase is high.
        
               | madeofpalk wrote:
               | And, JS engine's whims change frequently enough that
               | yesterdays micro-optimisation is today's deopt.
               | 
               | Much better to just write ideomatic code.
        
               | austin-cheney wrote:
               | That depends on how many changes it requires. If its just
               | a matter of don't do these 3 things and your code
               | suddenly becomes more predictable its like being slapped
               | with a magic wand. Everybody wins. All you have to do to
               | ensure 100% of your code compiles in a JIT is be
               | predictable. Predictable code is, on its face, always
               | less confusing.
               | 
               | > The performance benefits are likely to be minimal
               | 
               | This makes me cry, but not a cry of joy or ecstasy.
               | People guessing about performance is perhaps the most
               | frequent anti-pattern in all programming. Please read the
               | following document, you can skip to the end but it may
               | not make much sense if you do.
               | 
               | https://github.com/prettydiff/wisdom/blob/master/JavaScri
               | pt_...
               | 
               | When developers make random performance assumptions,
               | defensive assumptions are worse, it immediately
               | identifies a lack of professional maturity. Its like
               | small children playing games that expand their
               | imagination, which is great for small children but less
               | great for developers pretending to be adults.
        
               | afavour wrote:
               | > People guessing about performance is perhaps the most
               | frequent anti-pattern in all programming
               | 
               | I disagree, premature optimisation is.
               | 
               | Changing your style of programming to suit what todays
               | JIT does but tomorrow's may not, in anticipation of
               | performance issues you have not yet encountered is a
               | waste of everyone's time. No doubt it is valuable in
               | extremely performance sensitive code but that is the
               | extreme minority, especially in the JavaScript world.
               | 
               | If I were involved in a project where performance
               | concerns were high enough to require everyone know what
               | the JIT is and is not doing my proposal would be to use a
               | language other than JavaScript. If thinking this makes me
               | a "small child" in your mind I'm fine with that.
        
               | austin-cheney wrote:
               | What you are advocating, the guessing about performance,
               | is the premature optimization. Don't feel bad, most
               | developers have no idea what that term really means. Here
               | is the original essay where it comes from: http://web.arc
               | hive.org/web/20130731202547/http://pplab.snu.a...
               | 
               | Premature optimization is the extra effort required to
               | alter work necessary to circumvent guessed optimization
               | pitfalls. In the same breath Knuth advocates always
               | targeting known performance advantages.
        
               | afavour wrote:
               | > Don't feel bad, most developers have no idea
               | 
               | Some advice I know you'll ignore: your tone in the
               | comments here is deeply patronising. You know absolutely
               | nothing about me and yet are entirely comfortable
               | dismissing my perspective as wrong simply because yours
               | _must_ be correct. It's not an interesting or rewarding
               | way to converse, it makes me want to stop talking to you
               | as soon as I can. Which I'll be doing here. Have a good
               | weekend.
        
               | austin-cheney wrote:
               | Yes, I work in a language where junior developers and
               | people deeply unaware of the language advocate anti-
               | patterns and bad practice as matters of law, often for
               | personal defensive reasons. Its hard to not feel
               | patronized. You are correct in that I do not know you,
               | but I have been doing this work long enough to see when
               | people hide behind abstractions they don't understand as
               | necessary to mask their level of confidence and then
               | argue from for best practices from that perspective.
               | 
               | My best possible free advise: only advocate to what you
               | practice. If, for example, you have never written an
               | application in JavaScript without a framework, such as
               | Angular, then you are an Angular developer not a
               | JavaScript developer. If you speak to something you have
               | never practiced with a presence of authority people with
               | 10, 15, or 20 years experience will be condescending.
               | That is why imposter syndrome is a thing. Its better to
               | get the unexpected honesty from some irrelevant guy
               | online than get hired into a job and either get it in
               | person or worse compensating for a manager with imposter
               | syndrome.
        
               | afavour wrote:
               | This is exactly what I'm talking about. You are assuming
               | I am inexperienced and have no idea what I'm talking
               | about because I have a different view to you.
               | 
               | For what it's worth, I've spent years working with the
               | innards of various JS engines, managed deployments of
               | JavaScriptCore inside iOS apps (fun fact: no JIT) and
               | using QuickJS in low resource environments (no JIT there
               | either). There's an interesting conversation to be had
               | around optimising your code for JIT, given the different
               | environments we've both worked in. But your ego won't
               | allow it to take place. A shame for all concerned but in
               | my years of development I've met plenty like you and I'm
               | well aware that I'm not about to change your mind so I'll
               | just leave it there.
        
             | macguillicuddy wrote:
             | Perhaps the place I use it most is that try/catch is how
             | you deal with rejected promises that you've `await`ed in an
             | `async` function.
        
             | simlevesque wrote:
             | > Also, try/catch will never compile in the JIT
             | 
             | That's false. It used to be this way with Crankshaft but
             | since 2017 we have Turbofan which solves this.
        
             | eyelidlessness wrote:
             | > Also, try/catch will never compile in the JIT.
             | 
             | If this is an actual concern (as in you have measured and
             | try/catch is actually affecting something performance
             | sensitive): you can most likely mitigate the impact by
             | isolating the try/catch bodies in separate functions.
             | 
             | I haven't verified this in every JIT, but I saw meaningful
             | perf improvement in V8 for real world degenerate cases
             | where the performance did actually matter and make a
             | significant difference. But seriously, as always, measure
             | before optimizing stuff like this! It _might_ matter, but
             | it seldom will for anything more than academic curiosity.
        
             | raxxorraxor wrote:
             | For everything with the slightest bit of I/O it is quite
             | practical if you have even the most trivial assumptions
             | about its form.
             | 
             | Sure, you can check for everything. It often is more
             | effective, but not prettier or easier to read and of course
             | you also will miss cases.
        
               | austin-cheney wrote:
               | This sounds like some hyperbolic framework nonsense.
               | First of all I/O just means access to an API. I am going
               | to presume you mean messages from a network.
               | 
               | In that case a message from a network either sends or
               | fails and in the case of sending try/catch does nothing
               | for you but provide an error object, and you don't need
               | try/catch to capture the error. In the case of receiving
               | a network message the message comes in or it doesn't. If
               | the message does come in and is malformed your
               | application should be mature enough to handle that
               | appropriately to the benefit of the user instead of
               | crapping out some error messaging to the console. You
               | don't need try/catch to do any of this. A simple if
               | condition is more than sufficient.
               | 
               | > check for everything
               | 
               | You should check for everything. You only need to check
               | for one thing and if you check for it you are already at
               | 100% of everything. Did you get what you expected:
               | Yes/No?
        
               | thrashh wrote:
               | Disagree.
               | 
               | Usually you don't need to just know if there is an error
               | or not -- sometimes you need to know what that error is.
               | For example, an I/O error versus your image-is-too-small
               | error -- you need to respond to the user differently.
               | I've seen plenty of web apps that treat I/O errors and
               | user input errors as a generic "an error happened" and
               | that is completely wrong. Proper exception bubbling means
               | none of that code that encounters the errors needs to
               | necessarily know how to handle the error and your app
               | always responds correctly.
               | 
               | That said, I don't use exceptions as much in JS because
               | exception handling in JS is still very nascent. There's
               | no base library of exceptions and telling different
               | exceptions apart is not built-in to the language. In a
               | language with more mature exception handling, it's a
               | breeze.
        
               | austin-cheney wrote:
               | You made the same mistake as the grandparent by assuming
               | I/O means something more specific than it does. At any
               | rate there isn't a good reason to bubble error objects
               | out of the event handler and secondly even if you did you
               | would have everything you need to fully describe the
               | problem within the error object including the stack
               | trace, event type, error message, and so forth.
               | 
               | Its an equivalent breeze in JavaScript as well unless you
               | are fumbling through abstraction layers (frameworks) that
               | eliminate the information you need.
        
               | thrashh wrote:
               | Not at all. You don't have to describe anything when
               | throwing an error because that's the point of exception
               | inheritance. Second, the _whole_ point of exceptions is
               | to bubble them out of the handler(!) because otherwise
               | you would use return values (like in Go).
               | 
               | Since you are saying that you have to describe the
               | problem within the error object, it sounds like you must
               | be you are parsing error messages and stack traces to
               | figure out what the error is. That's not how exceptions
               | are to be used and I think that's why you are not seeing
               | why you would bubble errors.
               | 
               | In other languages, you don't have to do any of that
               | nonsense because object identity is much stronger, but
               | JavaScript uses prototypical inheritance so you can't
               | really tell if an error is an ImageError or an IOError.
               | Despite this issue, exceptions were added to the
               | JavaScript language without resolving that problem. The
               | reason why you have to worry about frameworks eliminating
               | error information is because that problem wasn't resolved
               | and so frameworks roll their own error handling and there
               | is no standard.
        
             | magicalist wrote:
             | > _Also, try /catch will never compile in the JIT_
             | 
             | This was only ever true in V8, and hasn't been the case
             | since 2017 with the optimizing compiler switch from
             | Crankshaft to TurboFan.
             | 
             | Take a break before lecturing commenters on "guessing about
             | performance is perhaps the most frequent anti-pattern in
             | all programming" next time :)
        
           | jve wrote:
           | Yes! This just drives me crazy:                  string
           | something = null;        try {           something =
           | mayThrow();        } catch (SomeException ex) {
           | log.Info(ex);        } catch (Exception ex) {
           | log.Fatal(ex);           throw;        }        if (something
           | != null) {           ...        }
           | 
           | Would be actually nice if it had F#'s try...with
           | https://learn.microsoft.com/en-us/dotnet/fsharp/language-
           | ref... and would allow us something like:
           | var something = try {          mayThrow()        } with
           | (SomeException ex) {           log.Info(ex);        } with
           | (Exception ex) {           log.Fatal(ex);           throw;
           | }
           | 
           | Or some way to do pattern matching on exceptions for switch
           | expression.                  var something = mayThrow()
           | switch {          (SomeException ex) => {
           | log.Info(ex);            return null;          }
           | (Exception ex) => {            log.Fatal(ex);
           | throw;          }          var passThru => passThru        }
        
             | scotty79 wrote:
             | var something = (() => {          try {            return
             | mayThrow()          } catch(SomeException ex) {
             | log.Info(ex);            return null;          }
             | catch(Exception ex) {            log.Fatal(ex);
             | throw;          }        })();
             | 
             | Close enough?
        
               | baq wrote:
               | not really; can't immediately return from the lambda
               | caller without throwing, always have to either try/catch
               | anyway or if/else the return value of the lambda. (also,
               | it's rather ugly to read and inconvenient to write.)
        
               | scotty79 wrote:
               | > can't immediately return from the lambda caller without
               | throwing
               | 
               | You want to immediately return in the middle of something
               | that was supposed to simulate just evaluating an
               | expression?
               | 
               | You can't immediately return in the middle of 2+2 as well
               | because return is a statement and can't be a part of
               | expression.
        
               | jve wrote:
               | Another idea, introduce ??? that would invoke on
               | exception :)                  var something = (mayThrow()
               | ??? (SomeException ex) => { log.Info(ex); return null} )
               | ??? (Exception ex) => { log.Fatal(ex); throw; };
               | 
               | Or maybe just start using Results as return type and get
               | ValueOrDefault :) But when it comes to handling
               | exceptions, I think it explodes there with ifs and
               | processing IENumerables:
               | https://github.com/altmann/FluentResults
               | 
               | But then again, simpler Results wrapper may be used
               | perhaps. But it is a different way of coding and takes
               | some mental shift on how to think about errors and
               | distinguish between error results and true exceptions.
               | 
               | Oh, csharplang already had discussion on the matters
               | about try/catch single statements:
               | https://github.com/dotnet/csharplang/issues/786
        
           | thdespou wrote:
           | Zig can though:                 const std = @import("std");
           | pub fn main() !void {         const stdout =
           | std.io.getStdOut().writer();         try stdout.print("Hello,
           | {s}!\n", .{"world"});       }
        
             | zkldi wrote:
             | that's not using `try` as an expression. can you do `let
             | foo = try <code> (plus some handling for diverging in the
             | other case?)`
        
               | flohofwoe wrote:
               | Yes, 'try' can be used like an expression in Zig which
               | resolves to the unwrapped success-result of an error-
               | union, or in case of an error immediately passes the
               | error-result up to the calling function.
        
               | kaba0 wrote:
               | It can and you in fact either have to mark that you
               | return that error when you use try, or you have to
               | provide a default value in a catch block:
               | fn potentiallyErrorReturningFunc() !u64 {         ...
               | }            const foo = potentiallyErrorReturningFunc()
               | catch { 0 };
               | 
               | One notable difference to most other languages is it
               | being a value only, basically a product type of return
               | value XOR error. These error values get serialized into a
               | number by the compiler that is unique, and on the type
               | system level you deal with sets of these error values.
               | Quite clever in case of a low-level system, in my
               | opinion.
        
               | flohofwoe wrote:
               | > const foo = potentiallyErrorReturningFunc() catch { 0
               | };
               | 
               | Nitpick, but AFAIK this won't work and needs to be
               | rewritten either to:                   const foo =
               | potentiallyErrorReturningFunc() catch 0;
               | 
               | ...or to:                   const foo =
               | potentiallyErrorReturningFunc() catch blk: { break :blk
               | 0; }
               | 
               | (I wish there would be a more convenient way to return a
               | value from a scope block, not necessarily with a
               | "dangling expression" like Rust, but maybe at least
               | getting rid of the label somehow)
        
           | xscott wrote:
           | What does a try expression like this do? Returns
           | null/undefined on a throw?
           | 
           | I suspect you could do something like:
           | const c = attempt(() => ... );
           | 
           | where attempt invokes the lambda, catches any exceptions, and
           | does what you want
        
             | lmm wrote:
             | > What does a try expression like this do? Returns
             | null/undefined on a throw?
             | 
             | If an exception is caught, the expression evaluates to what
             | the catch block evaluates to. (Similar to having if/else be
             | an expression). Of course if the exception isn't caught
             | then it propagates so the expression doesn't take a value.
        
             | afandian wrote:
             | Kotlin has this feature (`try` and `if` are expressions not
             | statements), and a good model for return values.
             | https://kotlinlang.org/docs/exceptions.html#exception-
             | classe...
        
           | lovasoa wrote:
           | With async code, there is                   const c =
           | myAsyncFun()                    .catch(e => ...)
        
             | WorldMaker wrote:
             | That leaves c as Promise which was not the type they were
             | looking for. You'd still have to unwrap the promise and
             | deal with the result or rejection. The easiest way to do
             | that is to use await, and if you are using await you'd be
             | better off using try {} catch {} around the async function
             | than .catch().
        
         | dejawu wrote:
         | I'm quite fond of this library for pattern matching; it's a
         | staple in all my new projects.
         | 
         | https://github.com/gvergnaud/ts-pattern
        
         | retrac wrote:
         | Some crude snippets from a Pascal-C (I never could decide)
         | self-educational compiler/toy I wrote a while ago (in Haskell):
         | -- a parser function that matches on { ... } returning ...
         | -- can be combined with other parsers          braces = (symbol
         | "{") `between` (symbol "}")          ...         -- a statement
         | is a controlFlow or a declaration or an assignment
         | statement = controlFlow <|> declaration <|> assignment
         | -- a block is either 1 or more statements surrounded by { ... }
         | -- or a single statement.         block = (braces $ many
         | statement) <|> statement         ...         -- An expression
         | is a number, or a variable, or a binary operation         --
         | derive functionality to print and test equality of these
         | values.         data Expr =              Num Int           |
         | Var Variable           | DualOp Op Expr Expr           ...
         | deriving (Show, Eq)         ...         foldConstants (DualOp
         | Add (Num a) (Num b)) = Num (a + b)         ...
         | 
         | Parser combinators (parser functions that take other parser
         | functions and return more complex combined parser functions)
         | with enough syntactic sugar can express BNF-like grammars
         | directly as code. And ML-style list and pattern matching
         | operations are very expressive for operating on the
         | intermediate representation.
        
         | xvilka wrote:
         | This is why once WebAssembly more polished and more common,
         | languages like JS and TS could become obsolete since proper,
         | more powerful languages will suddenly become a valid choice for
         | Web.
        
           | thrashh wrote:
           | What language would that be?
           | 
           | I've programmed in Java, Python, C, C#, TypeScript, VHDL, and
           | some other languages and they all fucking suck in some way.
           | 
           | If it was up to me, I would pick the best parts of the
           | languages/ecosystems and make a Frankenstein language.
           | 
           | And anyway in the end, it's never the language. It's the
           | ecosystem. Just because you can run Java on .NET doesn't mean
           | you should -- because almost no one else does it your way and
           | it's not worth the trouble maintaining some weird approach
           | very few people use. You're never going to get help from
           | library authors when it doesn't work on your weird setup.
        
           | mbork_pl wrote:
           | You mean, like C and its derivatives became obsolete once
           | successors to PDP machines were built? ;-)
        
           | andsoitis wrote:
           | > This is why once WebAssembly more polished and more common,
           | languages like JS and TS could become obsolete since proper,
           | more powerful languages will suddenly become a valid choice
           | for Web.
           | 
           | JavaScript and TypeScript are not "proper" languages?
           | 
           | Different languages are powerful in different use case
           | dimensions (tradeoffs), and the typical client-side web
           | programming has a very very strong UI angle, which many "more
           | powerful" languages that I am going to guess you have in mind
           | are less adept at.
        
             | airstrike wrote:
             | JavaScript's redeeming quality is that it is ubiquitous due
             | to being used by browsers. I don't think anybody credibly
             | claims it to be a language we would want to use if we were
             | to redesign web programming from scratch today
        
               | madeofpalk wrote:
               | Outside of the "everyone hates javascript" circle jerk, I
               | really enjoy writing Typescript. I find its type system
               | to be very natural and pleasent to use. It's my go-to for
               | any generic task.
        
               | marcosdumay wrote:
               | > I don't think anybody credibly claims it to be a
               | language we would want to use
               | 
               | Ouch, you posted this on the internet.
               | 
               | Somebody, somewhere is certainly going to claim this.
               | Some of the people with that claim are even going to have
               | a rationale behind it.
               | 
               | And to be fair, the tooling is going to push a lot of
               | opinion. Because as bad as the JS/TS tooling is, its UI
               | component has been evolving steadily while every other
               | tool stagnated. And by now it has quite probably
               | surpassed the 90's RAD paradigm (I mean, I can't make up
               | my mind).
               | 
               | IMO, the language is one of the main things holding those
               | tools back, but most people just won't agree.
        
               | shepherdjerred wrote:
               | TypeScript is a pleasure to use. I have no desire to
               | replace it with another language.
        
               | andsoitis wrote:
               | What follows is meant to be _descriptive_ rather arguing
               | "my camp is right".
               | 
               | > JavaScript's redeeming quality is that it is ubiquitous
               | due to being used by browsers.
               | 
               | JavaScript has other properties that make it very adept
               | at a wide range of web app usecases. Languages exist not
               | in isolation, but have a dynamic environment w.r.t.
               | domains they excel in, tooling, mindshare, programming in
               | the small and the large, comlexity, etc.
               | 
               | > I don't think anybody credibly claims it to be a
               | language we would want to use if we were to redesign web
               | programming from scratch today
               | 
               | Dart has tried. Many languages transpile to JavaScript
               | (and have for a long time).
               | 
               | Yet, JavaScript and TypeScript are some of the most
               | popular languages on the planet.
               | 
               | If the web is still around in 20 years, it will be
               | interesting to see whether another language has taken
               | center stage. I'm not quite holding my breath, if only
               | because even with transpilation being around for many
               | many years, no other language has overtaken. I have a
               | hard time seeing WASM changing that outcome in a
               | meaningful way.
        
           | flohofwoe wrote:
           | Any non-toy WASM application running in browsers will almost
           | certainly end up as a hybrid WASM/JS application, just
           | because you can't call directly from WASM into browser APIs,
           | and even if the JS shim is "hidden from view", sometimes it's
           | either more convenient or even more performant to write more
           | than just a thin API shim in Javascript, especially for
           | asynchronous code or to avoid redundant copying of large data
           | blobs in and out of the WASM heap.
        
             | LudwigNagasena wrote:
             | > just because you can't call directly from WASM into
             | browser APIs
             | 
             | Surely that will change someday.
        
               | flohofwoe wrote:
               | You are still limited by the fact that browser APIs are
               | mainly designed for usage with Javascript, and those
               | Javascript APIs usually don't map all that well to
               | languages used with WASM (which typically use the "C ABI"
               | for FFI). There is zero chance that each web API will get
               | a second API which is compatible with "C calling
               | conventions".
               | 
               | There is a (now inactive) "interface-types" proposal,
               | which has dissolved into "component-types", but this
               | scope-creep definitely is already quite concerning:
               | 
               | https://github.com/WebAssembly/interface-types
        
         | pjmlp wrote:
         | C# and Swift have pattern matching.
        
           | troupo wrote:
           | It's more limited than what you'd ideally want: pattern
           | matching in function definitions. This makes walking ASTs a
           | breeze.
           | 
           | Edit: And inline pattern matching for values returned from
           | expressions and function calls (similar to destructuring, but
           | more powerful).
        
             | pjmlp wrote:
             | I would say what C#11 does is already quite good, likewise
             | for Swift.
             | 
             | Perfection is the enemy from good.
             | 
             | Those I can use at work, ML derived languages not, even F#
             | is an uphill battle in most shops.
        
         | paddim8 wrote:
         | I would think C# is easier for this due to having proper (and
         | very convenient) pattern matching.
        
           | zkldi wrote:
           | C# doesn't have anything resembling proper pattern matching.
           | C#s pattern matching is akin to a marginally improved switch
           | or if/else-if chain. C#s pattern matches aren't exhaustive,
           | and the compiler doesn't properly check them at all.[0]
           | 
           | There are no proper sum types in C#, so 80% of the point
           | isn't even there.                   enum Season             {
           | Spring,             Summer,             Autumn,
           | Winter         }
           | 
           | ...                   int PatternMatch(Season season) =>
           | season switch {             Season.Spring => 1,
           | Season.Summer => 2,             Season.Autumn => 3,
           | Season.Winter => 4,             // compiler can't prove the
           | above code is exhaustive because no proper sum types
           | // compiler needs nonsensical branch here that diverges
           | _ => throw new ArgumentException("Invalid enum value for
           | command", nameof(command)),         };
        
             | hardware2win wrote:
             | >compiler can't prove the above code is exhaustive because
             | no proper sum types
             | 
             | Thats because c#s enum is not "closed"
             | 
             | https://github.com/dotnet/csharplang/issues/3179
        
               | [deleted]
        
         | chpatrick wrote:
         | Type-refining if in TypeScript is good enough for pattern
         | matching if you ask me.
        
           | graftak wrote:
           | The TypeScript type system meta programming actually does
           | pattern matching very well, it's just the underlying JS
           | runtimes that can't, there is a proposal for it though;
           | https://github.com/tc39/proposal-pattern-matching.
        
       | rustybolt wrote:
       | I really don't know what the author means by "language-centric"
       | vs "implementation-centric" and "production-ready"...
        
         | matklad wrote:
         | language-centric: the language you are compiling is flexible
         | (you are also the language designer), you want to build the
         | best possible language
         | 
         | implementation-centric: the language you are compiling is
         | mostly fixed and/or defined by an external entity. You want to
         | build the fastest compiler for this language, which emits the
         | fastest code.
        
       | zelphirkalt wrote:
       | But to what end? For a compiler, there is no need for all the
       | overhead of npm (usually), ts, tsconfig, package.json bundler and
       | bundler configuration, to get something usable, unless one really
       | wants a JS thing at the end to run in the browser. I imagine even
       | some webassembly tool chains may be shorter.
        
         | [deleted]
        
         | meheleventyone wrote:
         | The article answers this question. The author is using deno
         | which is a single binary that uses TypeScript without all of
         | that.
        
       | [deleted]
        
       | dna_polymerase wrote:
       | It sure is. For anyone looking into Compilers and just starting
       | out, I recommend this book: https://keleshev.com/compiling-to-
       | assembly-from-scratch/
       | 
       | The author uses a TypeScript subset to write a compiler to 32bit
       | ARM assembly and explains that it almost looks like Pseudocode,
       | so it is very accessible. A sentiment I can get behind, despite
       | avoiding it in any case possible.
        
         | Bognar wrote:
         | I would also _highly_ recommend Crafting Interpreters:
         | https://craftinginterpreters.com/
         | 
         | The book is split into two parts. The first implements a
         | language interpreter in Java, and the second implements the
         | same language by building a compiler to byte-code and also
         | implements a byte-code VM in C. Every line of code in the
         | implementation is referenced in the book.
        
         | jabits wrote:
         | Thanks for the ref. Looks very interesting!
        
         | jjice wrote:
         | Anecdotally, I love the reference to the classic "Dragon Book"
         | that this cover makes.
        
         | gavinray wrote:
         | This looks great -- thank you for sharing!
        
       | metalforever wrote:
       | It's just not the right tool for the job .
        
       | rtpg wrote:
       | TS's type system is fun but a part of me always wonders how much
       | faster TS's compiler would be if it was written in a compiled
       | language (assuming "good implementation", which is a big
       | assumption!)
        
         | smcl wrote:
         | I haven't used it in a couple of years (tbh I may have to
         | remove "full stack" and "Angular" from my CV...) but I don't
         | recall TS compilation being particularly slow. Are people not
         | happy with how quick it is, or do you have a particularly
         | big/complex application you're working with?
        
           | jitl wrote:
           | Notion is more than 10k typescript files and we view
           | typecheck slowness and memory pressure as an existential
           | threat to our codebase. Right now our typecheck needs ~12GB
           | of heap size but the memory needs have accelerated recently.
        
             | agloe_dreams wrote:
             | NX likely would do magic for you all but I imagine it is
             | far too late to do anything about that.
             | 
             | We had a very large Angular 10 project that we replaced
             | with an NX Angular project late last year. We went from ~6
             | minute prod compile to around 20 seconds, recompile is
             | lightning fast because it only builds your affected
             | library. That is all without using the remote library cache
             | feature where you can save compiled libraries to the cloud
             | such that a user only builds the libraries they are
             | changing ever.
        
             | cobbzilla wrote:
             | Can you not split the 10k files into modules for
             | incremental/parallel compilation?
        
         | supriyo-biswas wrote:
         | There's https://swc.rs/, a Rust implementation (albeit without
         | type checking at this time).
        
           | mcluck wrote:
           | Not really a fair comparison. Stripping out types is
           | trivially fast regardless of the implementation language. The
           | _vast_ majority of the time taken in tsc is because of the
           | type checking
        
             | mst wrote:
             | See the sibling comment
             | https://news.ycombinator.com/item?id=37173313 for a type
             | checker by the same author.
        
         | merlindru wrote:
         | Wonder no more: https://github.com/dudykr/stc
         | 
         | Written in Rust by the (lead?) dev of SWC
         | 
         | ---
         | 
         | SWC (speedy web compiler) compiles TS to JS
         | 
         | STC (speedy type checker) checks TS types
        
         | madarco wrote:
         | PS: swc and esbuild aren't good example, because most of the
         | speed improvements comes from the fact that they are just
         | stripping TS-specific syntaxes to generate JS code.
         | 
         | Also tsc is slow, sure, but only for the first run. Enabling
         | `incremental` flag or using watch mode with `--transpile-only`
         | usually brings compile time under 100ms, Making it practically
         | indistinguishable from SWC or ESBuild.
        
         | conaclos wrote:
         | Performance of the TypeScript compiler has greatly improved in
         | the last versions. The future isolated declaration mode
         | promises great improvements: until 75% of reduction in
         | compilation time! [0]
         | 
         | [0]
         | https://github.com/microsoft/TypeScript/pull/53463#issuecomm...
        
         | barbariangrunge wrote:
         | The reason we need to buy new hardware every few years, and
         | fill landfills with our old stuff, is because of ideas like
         | "let's use JavaScript to write a compiler!"
        
           | agloe_dreams wrote:
           | No lie, the M1 was a game changer for large Typescript
           | projects.
        
         | foldr wrote:
         | Probably not _that_ much faster. V8 is very good at optimizing
         | JS code. Typescript is slow mostly because of the complexity of
         | its type system (which is required to make it backward
         | compatible with existing JS code).
        
           | nikanj wrote:
           | Probably quite a bit faster if you do not have to instantiate
           | a new V8 for every single file, considering how many files
           | your average npm project has. I'm not certain if any
           | contemporary build systems reuse the compiler process for
           | multiple files
        
             | foldr wrote:
             | Why would it have to instantiate a new V8 for every file?
             | You can just run 'tsc' in your project dir to compile all
             | Typescript files.
        
               | harrygeez wrote:
               | I think people often underestimate how much the
               | TypeScript team does to make TypeScript fast and
               | efficient. They have some of the best people in the field
               | working on the type checker
        
               | foldr wrote:
               | Indeed. I haven't checked the implementation, but I
               | suspect that "don't spin up a separate process for every
               | file" is an idea that has probably already occurred to
               | them :D
        
               | alexvitkov wrote:
               | Yes, because it's one of the slowest compilers in the
               | world, and its job is to compile from JavaScript to
               | JavaScript. There are other languages with complex type
               | systems, where the compilers have to do _a lot of_ other
               | additional work on top of typechecking, and are still way
               | faster than TS.
               | 
               | And TypeScript is always slow, not only when you do abuse
               | its type system and require it to do complex inference -
               | I mostly use it for "this is a number" and "this is a
               | object with the following 4 fields" and it's still by far
               | the slowest component in my builds usually
        
         | agloe_dreams wrote:
         | The type system is fun right up until you are using it to its
         | full ability in generics..then you look at the 5 lines of 'type
         | logic' you spent the last day debugging and ask yourself how
         | you got here.
        
           | svachalek wrote:
           | I've definitely seen it suck people into a black hole. It
           | works best when you think of it as a form of documentation
           | with a benefit of enforcement, rather than trying to use the
           | type system to prevent every conceivable bug. Massive
           | unreadable types don't help programmers write better code.
        
         | cxr wrote:
         | > part of me always wonders how much faster TS's compiler would
         | be if it was written in a compiled language
         | 
         | TypeScript's compiler _is_ written in a compiled language.
         | 
         | (I think you are using "compiled" here as a euphemism--one that
         | doesn't help anyone.)
        
         | harrygeez wrote:
         | There's an answer from TypeScript team for your question :)
         | https://twitter.com/drosenwasser/status/1260723846534979584
         | 
         | Basically,
         | 
         | > Let's say TypeScript takes over 20 seconds to type-check a
         | medium-sized program. That's not usually because it's JS, it's
         | often because of types that cause a combinatorial explosion.
         | 
         | Also
         | 
         | > A different runtime can afford _a lot_ (it sounds like
         | parallelism and start-up time in this case) but I haven 't seen
         | a CPU-bound benchmark that supports the idea of a 20x all-up
         | speed-up.
        
           | rtpg wrote:
           | I mean I get what Daniel's saying, but I kinda doubt there
           | wouldn't be _some_ speedup. And hey, what if type checking
           | for small programs went from half a second to 100 ms? That
           | would be nice!
        
             | foldr wrote:
             | That's a five times speedup. I wouldn't expect a typical
             | 'compiled' language to run five times faster than
             | JavaScript on the kind of code you find in a compiler.
        
           | sdflhasjd wrote:
           | Both swc and esbuild claim to be much faster typescript
           | compilers in part because they're written natively, but are
           | actually just "cheating" and not doing any type-checking at
           | all.
           | 
           | That's not to say they're useless though, they're good for
           | fast hot-reload as you can have type-checking running in
           | parallel.
        
             | madeofpalk wrote:
             | it's easy to write a typed language compiler if you don't
             | need to actually check types :)
        
       | gr__or wrote:
       | Very much apropos:
       | 
       | Going between Rust and TS it is painfully obvious how much sth
       | like tagged enums are missing, which can also be seen in this
       | post.
       | 
       | I know of this [1] proposal for ADT enums which looks like it has
       | stalled. Anyone know of other efforts?
       | 
       | [1] https://github.com/Jack-Works/proposal-enum/discussions/19
        
         | dcre wrote:
         | I find discriminated unions work ok for similar use cases.
        
       | auggierose wrote:
       | Not that surprising:
       | https://news.ycombinator.com/item?id=37039443
       | 
       | Of course, that one has been downvoted to -4 (as of now).
        
       | catsarebetter wrote:
       | That is surprising, I would've thought that TS would have more
       | overhead because of the interfaces adding extra weight. Makes me
       | wonder what else TS could apply to. Language parsing, maybe?
        
         | recursive wrote:
         | At runtime, interfaces have zero weight. They get completely
         | compiled out.
        
       | frozenport wrote:
       | Gut reaction is to use LLVM for everything...
        
         | [deleted]
        
         | matt_kantor wrote:
         | What does that have to do with the compiler's implementation
         | language? A written-in-TypeScript compiler can emit LLVM
         | bytecode just like a compiler written in any other language.
        
       | paxys wrote:
       | The result shouldn't be all that surprising considering the
       | TypeScript compiler is written in...TypeScript. So TypeScript as
       | a ML is already battle tested and is in heavy production use
       | every day.
        
       | grumblingdev wrote:
       | TypeScript is an incredible language in general.
       | 
       | The fact that Functions are Objects that can have
       | properties/methods is supremely undervalued.
       | 
       | Are there other languages that do this so nicely? It's the
       | perfect blend of OO and functional.
       | 
       | Programming is mostly about gradually figuring out the right
       | design I find. JS/TS let's me evolve things naturally without big
       | rewrites.                   function foo() {}         function
       | bar() {}         function baz() {}         const commands = [foo,
       | bar, baz]         // Run commands         commands.forEach(x =>
       | x())              foo.help = 'This does something'         //
       | Describe commands         commands.forEach(x =>
       | console.log(x.help))              // Add some state via closure.
       | const config = {}         function foo() { config.blah }
       | // Add some state using partials.         function _foo(config) {
       | config.blah }         const foo = foo.bind(null, config)
       | 
       | I can flexibly do what I need, without ever having to define a
       | class like `Command`, which I probably don't even know what it
       | should be yet.
       | 
       | This premature naming of things in OO creates so many dramas.
       | 
       | It avoids things like `VideoCompressor#compress()`,
       | `VideoSplitter#split()`. You don't have to think: what state
       | belongs in which class...you just call the function and pass it
       | what it needs.
        
         | akkad33 wrote:
         | This is a Javascript feature, not really a typescript thing.
         | You can do the same thing with python
        
         | hazebooth wrote:
         | I could be wrong, but can't Common Lisp do this?
        
         | thrashh wrote:
         | TypeScript is really nice but you're really describing a
         | JavaScript feature to be honest.
         | 
         | Here's some other languages that support objects as functions
         | (although not necessarily functions as objects):
         | https://en.wikipedia.org/wiki/Function_object
        
         | [deleted]
        
         | _ZeD_ wrote:
         | >>> Are there other languages that do this so nicely? It's the
         | perfect blend of OO and functional.
         | 
         | Python, where everything is an object.
        
           | flylikeabanana wrote:
           | FP in python is painful without tail call elimination and the
           | higher-order function syntax is so clunky
        
             | nightpool wrote:
             | JS also doesn't have TCE, but for Python even just the
             | lambda limitations are surprisingly annoying. I can't tell
             | you how many times i've been frustrated because it's nearly
             | impossible to put a print statement into a python lambda
        
               | fullsailor wrote:
               | JS technically does have TCE (specified in ES6[1]), but
               | only the JavaScriptCore runtime used by Safari/WebKit
               | implements it[2].
               | 
               | [1]: https://webkit.org/blog/6240/ecmascript-6-proper-
               | tail-calls-... [2]: https://kangax.github.io/compat-
               | table/es6/#test-proper_tail_...
        
               | throwaway287391 wrote:
               | As a Python enjoyer, why do we want to shove so much into
               | lambdas rather than just doing an inline function `def`?
               | 
               | Is it the fact that you have to give it a name? If so I'd
               | say just using some generic name like
               | `{f,fn,func(tion),callback,etc}` is fine (at least as
               | much as an anonymous function is), and to me would
               | usually be more readable than an inline definition of a
               | multi-statement lambda would be.
               | 
               | Or maybe it's the fact that lambdas are allowed in the
               | first place, so people are going to use them, and then
               | when you want to debug a lambda you'll probably have to
               | go to the trouble of changing it to a function? That is a
               | fair complaint if so.
               | 
               | In any case I can see how it could be annoying if you're
               | more used to a language full of complex inline lambdas.
        
               | sixo wrote:
               | That's a mismatch between Python's choice of lambda
               | syntax and the use of whitespace instead of curly braces.
        
           | c-hendricks wrote:
           | In Python, can typings define a property added to a function?
           | 
           | Example, I have some Redux helpers that are functions, but
           | those functions also define a `.actionType` property.
           | TypeScript handles that.
           | 
           | edit: accidentally wrote "object" instead of "function"
        
             | nerdponx wrote:
             | The typical approach to this in Python is to define a
             | callable class instead. If you define the __call__() method
             | on a class, instances of the class will be callable.
             | class IntAdder:                def __init__(self, x: int)
             | -> None:               self.x = x                def
             | __call__(self, y: int) -> int:               return self.x
             | + y                def __str__(self) -> str:
             | return f"({self.x} + ??)"            add1 = IntAdder(1)
             | assert add1(3) == 4       print(add1)       # (1 + ??)
             | 
             | As mentioned in the sibling comment, you can define a
             | Protocol which is analogous to an interface, so if you had
             | a protocol like this:                 from typing import
             | Protocol, TypeVar            T = TypeVar("T")
             | class ValueUpdater(Protocol[T]):           def
             | __call__(self, y: T) -> T: ...
             | 
             | Then IntAdder would be considered a subclass of
             | ValueUpdater[int] by the type checker. Demo: https://mypy-
             | play.net/?mypy=1.5.1&python=3.11&flags=strict&g...
        
             | dataangel wrote:
             | I believe you could have a Protocol that requires the
             | property and that the object is Callable.
        
               | c-hendricks wrote:
               | Ah, that's not really that different than in TypeScript
               | then. There you have to define a callable interface, and
               | can add whatever properties you want
               | interface SomethingCallableWithAnExtraProperty {
               | // This means you can call it like a function.
               | (args: Whatever): SomethingReturned                // And
               | since it's an interface, you can still do interface-y
               | things           anotherProperty: string         }
        
         | thefaux wrote:
         | This blend of functional and oo programming was pioneered by
         | scala.
        
           | hocuspocus wrote:
           | Scala also supports structural typing, even though it's a bit
           | clunky and against its nature.
           | 
           | And advanced typelevel shenanigans you see in TS usually have
           | their counterpart in Scala, especially in Scala 3's meta-
           | programming features.
        
           | Zambyte wrote:
           | Lisp of course supported both decades earlier though :)
        
             | thefaux wrote:
             | Sure. Everything is reducible to lisp. But even though lisp
             | had the same capabilities decades earlier, scala is the
             | language that brought them to the mainstream (which may
             | well because of the historical accident that twitter's
             | backend was rewritten in scala). I don't love scala but it
             | has been hugely impactful.
        
               | koito17 wrote:
               | The meaning of "mainstream" changes throughout the years.
               | Remember that Common Lisp started as quite literally the
               | XKCD joke of "14 competing standards", except it actually
               | worked -- in the sense that this 15th competing standard
               | killed all the other ones and gained widespread adoption
               | in the Lisp community. Tons of vendors threw money and
               | effort at the situation, and it all started as an ARPA
               | manager's idea. Of course, we live in different times
               | now... But Zetalisp is one fairly popular Lisp I can
               | think of that had funcallable objects. Whether the idea
               | was appreciated or preferred over alternatives is a
               | separate thing entirely, of course. I would assume most
               | people would use a simple let-over-lambda to achieve the
               | same effect as funcallable objects.
        
         | andyjohnson0 wrote:
         | > Are there other languages that do this so nicely? It's the
         | perfect blend of OO and functional.
         | 
         | In .net methods, properties, member variables, classes, etc.
         | can have attached attributes that are objects. Attributes can
         | be interrogated at runtime using reflection.
         | [MyAttrib(foo=42, bar="baz")]         class MyClass         {
         | }
        
           | feoren wrote:
           | I really dislike attributes in C#. Their use is a big code
           | smell for me. They _look_ like C#, but they 're not actually
           | -- they're a part of the type system that has been disguised.
           | The fact that their parameters must be compile-time constants
           | helps perpetuate a harmful "primitive-centric" viewpoint that
           | is antithetical to many good design principals.
           | 
           | Their only reason to exist is to support a use case of a 1:1
           | relationship between classes and functional units (or methods
           | and functional units, or parameters and functional units,
           | etc.), which is almost always an 80% solution that makes the
           | other 20% extremely hard and/or impossible. I would go so far
           | as to say that every single use of an attribute is your own
           | code begging you for a better design. Unfortunately you're
           | sometimes stuck with them, but that's only because you're
           | sometimes stuck with a framework that itself is begging its
           | creators for a better design. I wonder if Attributes were
           | never added to the language, if developers would have just
           | made those better choices to begin with. From my vantage
           | point, they were a clear mistake in a language that was
           | otherwise very well designed.
           | 
           | They certainly are nothing like the ability to attach methods
           | to functions. A better example of that kind of convenience in
           | C# is extension methods, which you can certainly define on
           | types like Func<T>. I _love_ extension methods and miss them
           | in every language that doesn 't have them!
        
         | sisypheanblithe wrote:
         | >>> Are there other languages that do this so nicely? It's the
         | perfect blend of OO and functional.
         | 
         | Everything is an object in Ruby as well
        
         | CharlieDigital wrote:
         | The fact that Functions are Objects that can have
         | properties/methods is supremely undervalued.
         | Are there other languages that do this so nicely? It's the
         | perfect blend of OO and functional.
         | 
         | Yes. C#. The equivalent are `Func` and `Action` types
         | representing functions with a return and without a return. In
         | fact, the JavaScript lambda expression looks awfully familiar
         | to C#.
         | 
         | One of the snippets below is C# and the other is TypeScript:
         | var echo = (string message) => Console.Write($"You said:
         | {message}");                  var echo = (message: string) =>
         | console.log(`You said: ${message}`);
         | 
         | The same signature in C# and TypeScript:                   var
         | Apply = (Func<string, string> fn, string input) => fn(input);
         | var result = Apply(input => ..., "some_string");
         | var apply = (fn: (input: string) => string, input: string) =>
         | fn(input)         var result = apply(input => ..., input);
         | 
         | The C# can version can also be written like:
         | var Apply = (Func<string, string> fn, string input) =>
         | fn(input);         var lowercase = (string input) =>
         | input.ToLowerInvariant();         var result = Apply(lowercase,
         | "HELLO, WORLD");
         | 
         | For the curious, I have a small repo that shows just how
         | similar JavaScript, TypeScript, and C# are:
         | https://github.com/CharlieDigital/js-ts-csharp
         | 
         | Screen grab of the same logic in JS/TS/C# showing the
         | congruency: https://github.com/CharlieDigital/js-ts-
         | csharp/blob/main/js-...
        
           | nightpool wrote:
           | this doesn't really address OP's point, where in JS you can
           | do:                   const foo = () => doSomething;
           | foo.help = "this is a description of the function";
           | const commands = [foo];                  // print help
           | commands.forEach(c => console.log(c.name, c.help || "No help
           | is available for this function");
           | 
           | Presumably this isn't possible in C# because it's statically
           | typed, so the object returned by "() => doSomething" can't be
           | converted into one that supports adding more properties?
        
             | CharlieDigital wrote:
             | It can.
             | 
             | .NET/C# has a `dynamic` type (aka `ExpandoObject`). That
             | would be one way to do it (but would require casting to
             | invoke). It's not exactly the same since you'd assign the
             | `Func`/`Action` to a property of the `dynamic`. `dynamic`
             | is generally avoided due to how easy it is to get into a
             | pickle with it and also performance issues.
             | 
             | An alternate in this case is probably to return a tuple
             | which I think is just as good/better.
             | 
             | Example:                   var log = (object message) =>
             | Console.WriteLine(message);         var foo = () =>
             | log("Hello, World");         var fn1 = (foo, "This is the
             | help text");              var commands = new[] { fn1 };
             | commands.ToList().ForEach(c => {           var (fn, help) =
             | c;           log(fn.Method.Name);           log(help ?? "No
             | help is available for this function");         });
             | 
             | https://dotnetfiddle.net/szxb9F
             | 
             | The tuple can also take named properties like this:
             | var log = (object message) => Console.WriteLine(message);
             | var foo = () => log("Hello, World");                   var
             | commands = new (Action fn, string? help)[] {
             | (foo, "This is the help text"),            (foo, null)
             | };                  commands.ToList().ForEach(c => {
             | log(c.fn.Method.Name);           log(c.help ?? "No help is
             | available for this function");         });
             | 
             | https://dotnetfiddle.net/oPK1d8
             | 
             | Alternatively, use anonymous types:                   var
             | log = (object message) => Console.WriteLine(message);
             | var foo1 = () => log("Hello, World");         var foo2 = ()
             | => log("Hello, Neighbor");                  var bar = new[]
             | {           new {             doSomething = foo1,
             | help = "This is foo1's help text"           },
             | new {             doSomething = foo2,             help =
             | "This is foo2's help text"           },            };
             | bar.ToList().ForEach(b => {           var (fn, help) =
             | (b.doSomething, b.help);           fn();
             | log(help);         });
             | 
             | https://dotnetfiddle.net/KyBtgZ
             | 
             | The next release of C# (12) will include named tuple types:
             | https://learn.microsoft.com/en-us/dotnet/csharp/language-
             | ref...
             | 
             | Very much looking forward to this since it gives you a lot
             | of the same power of the JavaScript map/TS `Record`.
             | > ...because it's statically typed
             | 
             | While this is true, the `dynamic`/`ExpandoObject` is an
             | oddity and lets you do weird things like multiple dispatch
             | on .NET (https://charliedigital.com/2009/05/28/visitor-
             | pattern-in-c-4...). But C# has a bunch of compiler tricks
             | with regards to anonymous types that can mimic JS objects
             | to some extent. The tuple type is probably a better choice
             | in most cases, however.
        
         | notnmeyer wrote:
         | give me something with ts's type system and without exceptions
         | and try/catch. i know go's error handing gets shit, but i
         | really like the explicitness of it.
         | 
         | oh and burn npm to the ground please.
        
         | pull_my_finger wrote:
         | > Are there other languages that do this so nicely? It's the
         | perfect blend of OO and functional.
         | 
         | In Lua, you have regular, non-object functions, but you can
         | also create a callable table using metamethods, and keep
         | whatever data you have with it. Add to that the colon syntax
         | sugar (implicit self vs explicit self) on table methods and I
         | think you have a beautiful "opt-in" OO story without the
         | unintuitive `this` business from JS.
        
         | diegof79 wrote:
         | If you take out all the "script" legacy (type coercion which
         | was common for scripting languages when JS came out, the
         | initial lack of a module system which led to all kinds of
         | hacks, the scope of var declaration, etc), JavaScript and its
         | prototype based approach is really good.
         | 
         | In fact I wish that constructor functions, and the class
         | keyword never existed.
         | 
         | You can do the same with Object.create and a function closure,
         | isn't more verbose and it fits better with the mixed
         | functional/oop approach of the language.
        
         | paulddraper wrote:
         | > Are there other languages that do this so nicely? It's the
         | perfect blend of OO and functional.
         | 
         | Python                 compressor = lambda x: x / 2
         | compressor(5)            helpful_compressor = lambda x: x / 2
         | helpful_compressor.help = "compressor"
         | helpful_compressor(5)       helpful_compressor.help
         | 
         | Scala                 val compressor = (x: double) => x / 2
         | compressor(5)            object helpfulCompressor {         def
         | apply(x: double) = x / 2         val help = "compressor"
         | }       helpfulCompressor(5)       helpfulCompressor.help
         | 
         | Ruby also has .call...but I can't remember it well enough
        
         | asddubs wrote:
         | I don't like that at all. it all seems to be too informally
         | specified and requires divine knowledge to actually know how
         | you're supposed to use the magic function properties.
        
         | dgb23 wrote:
         | In Go, you can attach methods to functions (and any type
         | really, even "unboxed" primitives).
         | 
         | One of the canonical examples would be a net/http: A handler
         | can both be a struct (or any other type really) that implements
         | a serve method, or it can be a handler function that calls
         | itself to satisfy the interface.
         | 
         | In Clojure you would achieve the thing you describe by
         | attaching metadata on your function var. It being a Lisp, it
         | also has macros so you can pretty much do anything.
         | 
         | Speaking of macros:
         | 
         | Clojure also implements CSP channels with macros in core/async,
         | inspired by Go.
         | 
         | Channels are a very powerful construct that you might like a
         | lot. With channels you can completely avoid the function
         | coloring problem (callbacks, promises, async/await). Perhaps
         | most importantly they decouple execution from communication.
         | 
         | So going back to your example, your commands could be producers
         | that send their results on channels. They don't need to know
         | what consumes w/e they make, nor do they need to hold on to it,
         | mutate something, or call something directly.
         | 
         | Good analogies would be buses on motherboards, routers and
         | switches in networks, conveyor belts, message queues in
         | distributed systems and so on.
        
         | karmakaze wrote:
         | The same is achieved with Java's use of single-method-
         | interfaces. It doesn't matter what the method is called, it can
         | be used in function contexts without referencing the specific
         | method name.
         | 
         | I'm not sure I'd like my functions to have properties (which
         | gives them state and can alter what calling it does with the
         | same arguments). A big benefit of FP is getting away from OO
         | states. Perhaps the problems I work on aren't complex enough to
         | benefit from them, or I simply make objects from classes.
        
           | rezonant wrote:
           | To be clear, since "can be used in function contexts without
           | referencing the specific method name" might be ambiguous to
           | some- in Java you still call the "function" as if it was a
           | class implementing the interface. IE: `interface Foo { String
           | bar(); }` is still called as foo.bar().
           | 
           | It's just that there's now syntactic sugar for creating
           | anonymous classes: `Foo foo = () -> "baz"` that automatically
           | expands to an anonymous class that conforms to `Foo`. The
           | compiler automatically assigns the method name for you.
        
           | TOGoS wrote:
           | The properties don't have to be 'state' per se; they can also
           | be used to store metadata about the function. e.g. you could
           | have a function with 'domain' and 'range' on it, or an
           | 'integrate' method.
        
             | karmakaze wrote:
             | If the result of `integrate(...)` depend on `domain` and/or
             | `range`, what would you call that other than 'state'? Or if
             | `integrate(...)` doesn't reference `domain`/`range` what's
             | the use of it being there?
             | 
             | Or as I'm interpreting this, is sort of like documentation
             | or information that could be used at runtime for code
             | generation. Basically a shorthand for composing the
             | function with its metadata. The nice thing about separation
             | is that you know that when calling the function, there's no
             | possible way for it to reference the metadata that is
             | associated with it because it's composed externally.
        
           | grumblingdev wrote:
           | Interesting, didn't know about single-method-interfaces.
           | 
           | There are lots of singletons in OO. I find it useful to add
           | static metadata (not state) to them, without having to
           | escalate them to a class. I guess Java has decorators for the
           | same purpose. I'd really like decorators for functions in
           | TypeScript though.
        
         | tomp wrote:
         | This looks like plain JavaScript.
         | 
         | What does TypeScript add in this scenario?
         | 
         | Does the example above even pass typecheck?
        
           | usea wrote:
           | Yep. It's javascript, not typescript. Moreover, I think the
           | post is only trying to convey that they like dynamic typing.
        
       | boredumb wrote:
       | every day we stray further from God
        
       ___________________________________________________________________
       (page generated 2023-08-18 23:02 UTC)