400 comments

[ 2.7 ms ] story [ 292 ms ] thread
It's probably worth taking a step back and interrogating why the actual "Why was I Anti-TypeScript?" a little bit more and use it as an opportunity for broader self development.

The author didn't use and understand something, and rather than trying to they instead just defaulted to rejection. It's midly disapointing seeing this in people who label themselves as "Senior".

Agreed, though at least there's one less developer writing about the "TypeScript Tax".
I am a TypeScript fan but I do acknowledge the existence of the tax. TS is another cog in the machine that does useful work but can sometimes confusingly interact with other cogs (e.g. miles long unreadable TS errors coming out of React). Using or not using TS is not a trivial decision to me and should be made with consideration to the complexity of your stack.
(comment deleted)
I don't believe whether or not using TS has to do with the rest of your stack, but more about data consistency and the like.

I for one am building a large CRUD application with dozens of models spanning hundreds of fields, and Typescript is helping me (though not a panacea) keeping things correct.

I'm comparing it to a project I did years ago, first Backbone, then Angular, but both before 'typesave JS' was a thing, and that one relied heavily on reading and comparing keys and a deep knowledge of the domain.

But nearly ten years later, I don't have the mental capacity for that anymore and I need the crutches that typed languages offer me. By crutches I mean securities, safeguards and IDE assistance.

It makes sense for anything nontrivial IMO. Types allow you to be able to free up mental space. You can trust the type checker instead of having to rely on your own abilities.

Can relate about illegible React stack traces in production builds. One of the reasons in my case was Create-React-App obfuscating ES6 class names, which is just insane! Just why???! Why throw all code structure away just to save a couple KBs. The worst part is CRA folks acting like dictators when asked to provide an option to toggle that: "Why in heavens earth would you need that?". "You can fork our 30K+ line repo [just to toggle minification] if you want".

Sorry for the off topic rant!

P.S: Yes I know source maps exist. They suck/aren't reliable.

You could also use customize-cra and skip on minifications in about 10 lines of code
The heuristic of "new JS tech is just needless complexity/over-engineering" is not a bad one to have in the JS ecosystem imo :)
Typescript is hardly "new", it's fairly widely accepted now. It might be worth thinking "why do all these people enjoy using Typescript?".

or idk just say that no, its the kids that are wrong.

All the kids enjoyed using Javascript too. Language popularity is a bit of a random walk.
I mean, Coffeescript was fairly widely used at its peak, and now it's all but dead.

Front-end fads are fickle. At least TypeScript has Microsoft backing it so they'll likely continue to support even if the rest of the webdev world moves on to the next shiny object.

I don't think I've ever heard of any in-production, large apps in Coffeescript, even from when it was at its peak. I know of a handful of companies that tried it and even enjoyed it quite a bit but none of it was for mass consumption.

Typescript, meanwhile, is used for hugely popular apps, is used by very large companies, has Microsoft's full backing, etc.

The two are not comparable.

Edit: Clearly I'm misremembering its popularity.

Wasn’t CoffeeScript the preferred way to write JS in Ruby on Rails for years?
I think the Rails resource generators still default to creating Coffeescript files. I could be wrong though — I switch between Rails 4,5 and 6 projects right now. I’ve been on over a dozen Rails projects at different companies and only ever worked one project that used Coffeescript in production, and that was an app built pre-2010.
HAML and coffeescript.
I miss the elegance of Haml. Moving from an indented syntax for DOM literals to the likes of JSX felt like a step backwards.
Maybe for you, but that is not a popular opinion. JSX's rigor is a breath of fresh air in a world that constantly generates low quality html due to loose constraints. HAML had those same problems.

JSX is kind of xml done "right". Differences between children and attributes in xml is very hard to grasp, but JSX has a much better defined structure to it and is super well suited to generate html because of that.

This sounds like an orthogonal point. (I agree with it, so it feels odd to see it presented as a rebuttal.) Surely an indented syntax could have rigor if we wanted it to?
A significant portion of Airbnb was written in Coffeescript, including most of the complex app-like stuff used by hosts to manage listings and calendars.

We ported off of Coffeescript when ES6 and Babel came out, and you could start writing `class` and using `() => {...}` function expressions with lexical `this` scoping.

Coffeescript lost because it stood still next to the ecosystem and next to the new Ecmascript language standards, which obliviated its advantages. The only advantage Coffeescript had over ES6 was “some of your code is already written in it”.

Once Ecmascript formalizes a type system, we’ll see the same process happen with Typescript. But, I think it will be many years before that sort of feature is standardized. In the mean time, I’m happy to use Typescript today.

PagerDuty’s front end was 100% coffeescript until at least 2016
Square's entire merchant dashboard was in CoffeeScript. Half a million lines of code.

Took over a year to convert to ES6.

I also have fond memories of painstakingly converting hundreds of coffeescript files back to JS. There's something to be said for using the native language.
Browser support and consistency got a lot better.

JavaScript was a minefield of compatibility problems. Many intentional, cough: Microsoft.

Jquery was awesome because it dealt with most of that

JQuery dealt with DOM and browser stuff. Coffeescript dealt with syntactic language stuff. I can't think of a case where Coffeescript helps with the same thing JQuery does, although it may exist.
Yeah as best I can tell, Coffeescript was a thing because Ruby was very popular at the time, and some Ruby devs had to write JS but they wanted to write Ruby.
I think everyone except JS devs wishes they could write something other than JS for the browser.

Maybe eventually with WebAssembly...

That was definitely the case before ES2015. I think many of the non-JS devs that still hugely wish they could write something other than JS for the browser haven't kept up with ES2015+ nor Typescript. ES2015 is a much improved language and vanilla JS isn't painful anymore, especially with type="module" support now green enough in caniuse statistics that we can finally kill AMD and CommonJS for good in greenfield vanilla JS projects. The sky is quite sunny and ES2015+ is a better language than a lot of non-JS devs think JS is.

It will still be a while before all the brownfields in the larger ecosystem get cleaned up (if some of them ever do), but that's no longer a language problem, that's a a long tail ecosystem problem.

Yup. I don't find myself using classes much, but some of the other new features remove the vast number of issues you tend to face.

Is scoping an issue for you? Just use arrow functions!

Is callback nesting an issue for you? Just use `async/await`!

I love it so much.

I use classes a fair amount, but there's still some weird stuff in the language I'd take out if I could.

Sparse arrays, all object keys are strings (or symbols), crazy coercion rules, the existence of `null`, weird float vs int rules.

You can use `Map` instead of object which allows keys of any type, or typecheck your objects with `Record<K, V>` and let typescript warn you about any weirdness. Typescript can also warn you if you do some weird implicit coercion. You can typeguard against `null` (although I admit it is annoying, I just want to use an optional†). And if you really want to use integer types there is always `BigInt`.

---

†: I haven’t checked, but I bet there are some libraries out there that provide proper optionals using Proxy.

I use BigInt often, but you can't (for example) do most substring operations with it, even though floating point indexes make no sense.

Map goes part of the way to what I want. But I really want to control which objects are considered equivalent keys, rather than being limited to reference equality.

There's a Stage 2 proposal before TC-39 to add immutable Record and Tuple types [1] which would be structurally compared and perfect for complex Map keys.

It's also easy enough to find or write quick simple Map wrappers that use a hash function on an object as keys when provided (either piggy-backing on the existing Object.prototype.valueOf, which always exists on every object and easily falls back to the reference-based current behavior, and expecting classes to have custom overrides for that, or using a Symbol named function of their own to avoid polluting own-property-keys/name clashes with other libraries).

[1] https://github.com/tc39/proposal-record-tuple

Scala.js actually works really well. Frontend people aren't very aware of it, but if you're a backend dev who just needs to write a bit of code that has to run in the browser then I highly recommend it.
Even as an engineer happiest in Scala almost forced to write Typescript, I'll be the first to admit that the Scala.js library is still a bit lacking in comparison for real world productivity.
I had exactly the opposite experience. I kept trying to make Typescript work and it just wouldn't, eventually resorted to Scala.js and found (to my shock) that everything just worked and there were far less random rough edges.
That’s great :) Scala.js has such potential. I hope that my next opportunity to try it out (versus TypeScript) will be a similar experience to yours!
I was never a fan of the Rubyisms in Coffeescript. But it's sad nothing else ever implemented the YAMLisms.

Typing up constants in Babel JS is so much uglier than it needs to be.

But Coffeescript also introduced things like arrow functions that bind `this` to JS. Transpiling to ES5 allowed JS to evolve.

In my private projects I always write in JavaScript and provide static typing using JSDoc comments (which typescript can check). Then I run the type checker with `--noEmit`. I have yet to run into a problem with this approach.
Coffeescript was a good thing when we only had ES5. In fact many of the things it championed made it into ES6 and beyond. I think with TypeScript it isn't just a shiny thing. If it does substantially change I believe it will be to break the forced compat with JS. As WebAssembly gets more adopted it will be possible to not have JS targets, and thus remove a lot of the weird JS things that have been kept for compatibility. Typescript has the gravity to continue.
CoffeeScript served a purpose, and was not a fad. It got deprecated by an ECMAScript that adapted most of the good parts.

TypeScript brought something good for the JS ecosystem (although Flow did it too, in a different way) and it doesn't look like anything will be able to replace it at what it does anytime soon.

I am really hoping TC-39 starts considering optional static types (similar to python), using a simpler syntax then TypeScript (e.g. no enums). However I don’t have high hopes they will any time soon.
Typescript is not accepted. How AngularJS stuck on 1.* is the best proof, and an illustration for the problem. Pretty serious people are working in Angular community, it's immensely popular in the enterprise webapp space, so I will not take the "Ah, it's just amateurs who are stuck there."

It adds to countless attempts of other transpiled *scripts to extend Javascript.

They, and Coffeescript in particular, are good historical references to what will happen to TS in a few years.

I don't recall most major projects shipping with support for Coffeescript. Looking at my projects, the amount of "@types/*" I have are pretty small compared to a year or two ago because most projects ship types directly in their package now. Personally, I think it's here to stay.
About 95% of React components I see in the wild are in TypeScript. The most rapidly growing JS backend framework (Nest) is in TS. Deno, the evolution of Node, is based on TS. Pretty much everything in the JavaScript OSS world has TS options or is based on TS.

I doubt Coffeescript ever saw this scale of adoption.

We only use TypeScript on frameworks that make it a requirement, the language is starting to look like Haskell.

As much as I like Anders work, it is impossible to understand modern TypeScript without looking into tsconfig.json before looking into source code.

I don't understand what the tsconfig has to do with understanding the source? Maybe when using different path resolving or different tsconfigs to compile test files vs. production files?

Many toolings make it so you don't even need to worry about the tsconfig anymore (though in reality it is very helpful to understand it).

To avoid breaking existing source bases, many features or corrections across the language versions are opt-in, so in some projects it looks like GHC feature pragmas.
This does not align with my experience of writing Typescript at multiple companies of varying sizes and open source projects.
Lucky you for not having devs that would rather be using Haskell Lens and stuff like that.
(comment deleted)
Looking at GitHub statistics, I would dare say TypeScript is pretty accepted: https://madnight.github.io/githut/#/pull_requests/2020/4

Out of all pull requests on GitHub in Q4/2020, 18.8% were JavaScript and 6.7% were TypeScript.

For reference CoffeeScript was at 0.25%. At its peak in 2014 it was at around 1.5%, when plain JavaScript was at over 20%.

I wrote AngularJS 1.x in TypeScript in an enterprise application. Being "stuck there" ("there" meaning pure JS for Angular 1.x) is a choice.
I'll second that. We used AngularJS 1.x with TypeScript in an enterprise application.
I took over a legacy AngularJS 1.x MEAN app early last year. One of the first things I did was to convert it from using Gulp to building the client with Create-React-App's build tooling:

https://blog.isquaredsoftware.com/2020/03/codebase-conversio...

That unlocked the ability to add React and TypeScript code embedded directly in the existing AngularJS UI, using the `react2angular` library, as a short-term migration approach. I was also able to migrate some of our existing AngularJS controllers and client logic to TS to get a bit more safety.

Since then, I've set up a new Next.js subproject that sits behind the existing Express app server, and was able to show content from Next by inserting iframes into the AngularJS templates and proxying those URLs from the Express app to the Next app. That's let us build new features from scratch inside of the Next codebase while showing them seamlessly in the AngularJS UI, giving us a long-term migration path.

I'd really like to hear what alternatives you have in mind for code in the JavaScript family that needs to have types. At my workplace, types are an important part of our workflow.

We've been using Flow as a type checker but are converting our code to TypeScript for the tooling, ecosystem, support, and hopefully improved performance.

TS has been running as a project for quite a while and a whole lot of folks have a stake in it. I think it's a pretty good bet.

Additionally, I was there when Angular 2 was announced, and the community had no idea what to do or when it was coming out and didn't know whether to dig their heels in and stay on 1 or what the migration path to 2 would look like. I think that's a slightly different story given the context.

> Coffeescript in particular, are good historical references to what will happen to TS in a few years.

The thing is that many good ideas of Coffee ended up being integrated in newer versions of JS, so of course the need for it decreased; one could argue that Coffee's legacy lives in ES201x.

But there are no types in JS, and none planned.

Yeah, it would be great if TC39 made type hints syntactically valid but semantically ignored (ala Python's PEPs to that effect) so that type erasure was no longer needed as a "build step", but so far TC39 is still hugely aware of the controversies surrounding ES4 (the "lost" version of JS) and still very hesitant to do anything type related in JS.
I myself am quite fond of it actually! I was initially suspicious of Node at first and of TS as well, but like the author, I eventually gave it a try and liked it a lot. (Probably after many of the pain points had been worked out!)
That's my personal heuristic and why I continue to reject TS just like I avoided coffee script, flow, clojurescript, and so on.

But this article reads like "Developer who only used dynamic typing learns about static types." You could sub in any two such languages and get the same article.

One of the things you listed is not like the others. Why didn't you give Clojurescript a chance. It's like being in Hell and then someone brings you infinite amounts of cold fiji water. Hell becomes more bearable ;)
Clojurescript is the worst offender on that list from the point of view of "needlessly complicated." I can't imagine a dependency I want less than the Google Closure tools. I swear Closure is probably the only tool with fewer devs who understand it than autoconf.

I'm not critiquing any of those languages, in a perfect world any of them in the browser would have been preferable to Javascript. The issue is the tower of dependancies we are building, and you have to stop somewhere.

With advanced optimisiation turned on, the Clojurescript compiler will not include any of the Closure code that you do not use. But my point was about language superiority. For me it's obvious why Clojurescript wins the client side.
My issues is not the runtime dependency but the development dependancy. As the production apps I am responsible for keep getting older and older my opinions on ruthlessly cutting decencies get stronger and stronger.

Somewhere there's probably an optimal expected lifespan of a program where the line between preferring a better language and fewer development dependencies is crossed, but I'm also starting to learn that software stays around years longer than I ever anticipated.

Heuristics shouldn‘t really be a factor when you are a senior. Make a decision based on your competence and experience.
Yes, type checking do not require a new language.
I find your interpretation and subsequent extrapolation on his qualification as senior unfair. He clearly tried TS and was frustrated by his initial experience, because there were certain things he had a hard time understanding or justifying. There's no opportunity for broader self development here. I try stuff out and I scratch my head, should I risk sticking with it or should I move on. It's a gamble. Others have made the alternative bet with a different tech du jour and lost. So your results may vary.
I think moment when I really could call myself "senior developer" came when I realized annoying things I was struggling with are not stupid, it is just that I don't understand them very well.
My senior moment came when we were talking about graphics and I mentioned some various things and CGA. My coworkers let me ramble a bit and then one politely asked what CGA was.
I’d consider myself senior. I’ve been using Javascript since it was beta. I like Javascript. I don’t mind TypeScript. I mean, it’s nice and all but it doesn’t strike me as the be-all that others make it out to be. Maybe I just don’t use it for the types of project for which it was designed.

It’s like I’m a metal worker and I get along fine with a ball peen hammer. You come along and give me your claw hammer with great excitement. Sure, it can mostly do the same job, but my ball peen hammer is what I’m used to so I’m sticking with it.

I understand the sentiment, but a better comparison would be if your tool of choice had to be shared between everyone on your team.
Would that imply that the GP needs to reconsider their willingness to use TypeScript, or is it just a friendly reminder that there is utility in finding teams with shared values?
Senior does not mean "all around great zen person". It means an experienced person.

Seniors occasionally rejecting this or that, seniors occasionally blindly accepting this or that are both completely normal in majority of seniors.

Senior probably means they've been doing JS for decades before TS was invented. They got along just fine without it for so many years, why rock the boat now? Or maybe it is more of a Senior person has a ton of work to do and not enough time to do it in so there isn't time left to learn a whole new way of writing JS. Or maybe many of the errors TS is meant to catch, the Senior developer has already learned over the decades not to do that bad thing, so they don't see the need to adopt TS. Or maybe they are just jaded and tired of learning new things.
> Senior probably means they've been doing JS for decades before TS was invented.

JS has only been around for 25 years. I’m a “Senior person” and I’ve been seriously working with JS for less than 5 years.

Well, I'm Senior too and I've been working with JS for 23-24 years. So I guess I'm projecting my own experience.
I cannot wait to be the type of developer who just never makes typos.
Your IDE should be catching any typos. But it's not about typos, it's about understanding the quirky nature of JS and knowing what things not to do. For instance, an inexperienced developer might try to use .sort() to sort an array of numbers. It wouldn't even occur to an experienced developer to try that since they know from vast past experience that it won't work (or, at least, won't do what the inexperience developer is thinking it might do).
Does does my IDE know what functions should be called without types?
Why not use something if you have an added benefit? It‘s like writing code in „ed“ just because it worked for decades
I've been programming JS for 13 years (professionally, and some more as a hobby) and I'm pretty aware of its warts. And as a firm believer of TDD, most of my code is pretty well tested.

Unit tests have similar advantages to types: they catch errors early and let you refactor your code with confidence.

But unit tests go even further, because they test the _correctness_ of the returned value, not just its type.

And a massive turnoff for me is that the types are compile-time only.

So you see, if your code is tested, and the types are just partial static checks; I feel like it's just not worth it.

I enjoy using types in other languages, for example it feels nice to add type hinting to old PHP codebases. But in PHP, types are much simpler (less work) and more effective.

If they added optional run-time instrumentation like "spec" in Clojure, I would definitely use TS. But for now, I'm out.

You should be testing regardless of the strength of your type system.

But testing with a weak type system isn't going to give you anywhere near the guarantees you'd get from a well-designed type system in addition to testing: https://kevinmahoney.co.uk/articles/tests-vs-types/

I think the skepticism of new shiny things is healthy. Of the set of things that exist, most things are worse than the things one already uses/prefers. Most new things solve a specific problem that may not overlap with the problems one is trying to solve. With software development, a huge part of the puzzle is tooling (as the article points out), so even if typescript is obviously superior as a language, the tooling needs to catch up before it's a reasonable/useful alternative. In this case, the tooling quality, enabled by the static typing, shot typescript way past the thing it was replacing.
Strong typing is not shiny or new and, even if you're a javascript guy, not being remotely familiar with any strongly-typed language (or generics?!) is a red flag.

(edit: red flag is a bad description, because he ended up being self-reflective and looking deeper, which is just about the furthest thing from a red flag.)

Agreed. But some people have an attitude of Strongly Typed == Needlessly Verbose. Probably because most developers have only experienced Strong Typing via Java.
I'm in neither camps (I don't have anything against types in places but I also don't try to put them everywhere). Could you explain how TypeScripts types would be more/less verbose than the types from Java? At a glance, TypeScript looks a bit more flexible, but mostly the same.

Then Java is a verbose language in general (forced directory structure, one-class-per-file and yadda yadda) but that's besides the point as you're pointing to TypeScripts types being less verbose than Javas.

1) TypeScript has local type inference, meaning that `let x = returnsComplicatedThing();` will work, where in Java it would be `Complicated<Generic<Type>> x = returnsComplicatedThing();`. (At least up until the last few years)

2) TypeScript has type aliases, meaning you can write `type MyTable = List<Map<string, number | string | Foo>>` and not have to repeat yourself everywhere.

3) TypeScript has anonymous unions and tuples, so instead of writing `Triple<Integer, String, Foo>` you can just write `[number, string, Foo]`, and likewise instead of `OneOf<Integer, String, Foo>` you can write `number | string | Foo`. These are also built in and very ergonomic, so you don't have to write a OneOf4 class whenever you want to have one of 4 things, and you don't have to write helper methods to convert between different sized OneOfs.

Don't know about the other two, but the first one is available in java 10 and later.
Worked on a project with an old (12+ years?) - but still being developed/used - Java system. Just last year a move was made up to Java 11 (from 8). Decent test coverage, but moving multiple systems is still a big task, but... they did it. Java 11. Yay.

But... teammates were running in to issues with 'new' stuff - like, using 'var'. There were others, but this was sort of the archetypal argument. "Well, it doesn't match the rest of the style of the file/app - it'll make it hard to read". Well... hrm... any new feature literally doesn't match the style of what came before it, because you have new keywords/features/syntax to handle processes a new way. It's definitional. But just because there's a new feature doesn't mean it'll be adopted (for better or worse, I supposed, depending on your stance).

Java-the-language/JVM has picked up pace hugely in the last few years, but Java-the-ecosystem tends to lag quite severely. Not least because these systems tend to be entrenched systems that move quite a lot of money around... or are highly business critical in some other way. This leads to extreme conservatism wrt. upgrades, etc.

(That part has also gotten better, but especially the Java 8 -> anything later upgrade has been extremely painful because of modularization. Past Java 9 it usually isn't that much of an issue. However, JDK 11 was the first LTS release after 8, so most shops basically delayed the transition until JDK 11.)

(comment deleted)
This BS made me want to tear my hair out:

    VerboseTypeName foo = new VerboseTypeName();
The reading or the writing part? The reading is easier with explicit types. The writing is a non issue considering tooling auto completes the second instance almost always. modifying later can be cumbersome but the tooling makes it somewhat easier too.
Both, and not interested in tooling to help me repeat things. :-)
This, and usually Java from 10-15 years ago before the `var` keyword and diamond operator.
I definitely fell in this camp. To be fair I was fairly junior and was used to JS.

It wasn't until I started using Haskell that I saw the benefits of knowing exactly what I was working with. It also helped me be more deliberate and thoughtful as you had to define everything up front.

Strong typing can be both needlessly verbose and useful, or the total opposite. My experience with java compared to Typescript, is that the tools largely compensate for Java's verbosity, while that is less true of TS or JS. For example, generating strongly typed method definitions from field declarations.
> red flag is a bad description, because he ended up being self-reflective and looking deeper

No, red flag is fair since it's just a warning sign. Even if we take it as a mark on the guy, it's true that he ought to get outside the scripty bubble.

I built websites and wrote JavaScript for a long time before discovering generics by returning to school and being forced to build data structures from scratch in java. Other than that, there's no way I'd have ever come across them. I was just turned down from a senior position because someone had more years of React experience, so we're not exactly in the most sophisticated hierarchy here. Seniority should probably not depend on your exposure to technical language mechanics, especially those that weren't related to your area of expertise.
The author actively describes themselves as being "Anti-Typescript" and "hating" something they have no experience with. That's not skepticism.
Being skeptical shouldn't mean being ignorant. Being skeptical should mean Trying to understand the pros and cons and believing it's not worth it.
Or alternatively waiting it out a bit until it's more obvious what the pros and cons are (because others have tried it out in depth and shared their experience). Still a far cry from actively dismissing it.
Also more efficient. There's an endless, constant supply of new things not yet worth fully exploring.
Agreed. A senior engineer should know when they don't understand something and keep an open mind about it rather than rejecting it outright.

If this article started with legitimate reasons for not wanting to adopt TS it would be different. "It is hard for people to learn" is actually a legit reason but "I don't understand it" is probably not!

That doesn't imply all new things are good or that senior engineers need to understand all new things either. I think there is some confusion about this.

Skepticism !== reject everything without grokking.

I think that he is presenting his prior attitude towards typescript a certain way for dramatic effect / so that it was easier to write a blog post about it. In reality he probably was not as skeptical as he sounds here -- otherwise we wouldn't be reading an article by him about how great typescript is.
Ironically, as someone who has experience w/ both fast-and-loose JS and with "properly" statically typed languages, my beef with typescript is precisely that it allows people to be fast and loose, sometimes in ways that are not super obvious. For example:

    type Thing = {
        name: string,
    };

    function getThing(): Thing {
        return JSON.parse('{"error": "invalid id"}')
    }

    const thing: Thing = getThing(); // lie to me!
    console.log(thing.name);
This is a trivial example to illustrate the idea that it's relatively easy to make a type that cascades through a very large app but which is backed by a complete lie.

I definitely think that there's also something to be said about meta-programming yak shaving (e.g. people wasting hours trying to write a "function" that takes an enum and outputs another enum w/ a fixed prefix, when simply writing out the enums would've taken 30 seconds w/ VSCode's multiple cursor feature)

Personally, the value I see in TS is in cementing documentable facts about a system: e.g. such and such function takes such and such well-defined entity, dot not treat this `options` argument as a generic bucket for pass-through garbage.

If you have to rely on restrictively typed languages to produce maintainable code, I think that’s a different topic.

Typescript isn’t perfect by any means but it does a good job keeping code based easier to work with in a larger team. Much better than vanilla JavaScript

The "curse" of Typescript is that it's built on Javascript, so you can tell it to lie to you all the time. This is the problem with introducing Typescript to a larger group of Javascript developers who arent all on board (because of either experience or being stubborn) - you still need to establish good habits and patterns for how to write typescript.

I'm working through the very lie you're showing now because the disclipline wasnt established from the start to avoid these anti-patterns. Typescript isnt the perfect magic bullet that will disappear all your problems - you still need to have good developers writing good code, but TS will make it easier.

In the large this curse is true for everything. The difference between 8 u8s and an f64 is thin for all architectures I can think of.
> you still need to have good developers writing good code, but TS will make it easier

Very true. Typescript is still immensely helpful in the sense that it will complain if you pass a Thing to a function expecting a Foo, regardless of the fact that Thing is meant to be a thing but is actually an error.

But IMHO, by sticking to a decision to be lean in terms of transpiled JS, TS suffers greatly from cases like the one I showed. It would be great if the compiler enforced that these types of unsafe casts had to be explicitly specified (even if via some strictness flag), and even better if it could do a better job at refining types through runtime constructs like `if (typeof thing.name == 'string')` (it does do it to some limited extent, but not enough to cover many common scenarios, due to the curse of targeting Javascript, as you mentioned)

You can easily write your own "type guard functions" when you think TS is missing an inference from a runtime check you would like. The syntax is simple enough, it's just a slightly different return type from boolean:

    function isThing(value: unknown): value is Thing {
      return typeof value === 'object' && typeof value.name === 'string'
    }
The `unknown` type in general is still pretty new, but will be greatly helpful for cleaning up a lot of code that assumes a type or used to use `any` or `{}` because there wasn't a better type to use in that situation. The Typescript team is hesitant to change the last few cases of `any` in lib.d.ts to `unknown` directly (such as `JSON.parse`) because that would be a big compatibility breaking bug. There was a proposal for a flag to do that (treat `any` as `unknown` in lib.d.ts), but I'm not currently aware of whatever happened to that idea. I would love to see it or something like it happen.
User-defined typeguards are just fancy ‘as string’. They are not type safe.
They localize all the unsafe type casts to one place, where you can inspect the logic, though. Much better than having `as string` sprinkled throughout your code.
It's worth noting that this (good) example is unrelated to unsoundness, too -- it's related to built-in APIs using 'any'.

Within Google we include a "strict" d.ts in every compilation that shadows this API (and one other) with one that instead returns "unknown". We have talked about going through the whole TS API and removing all the 'any' in this manner for this reason.

This is an argument against strongly typed languages, not Typescript. You could do a similar thing in Java and C#.

I don't know of any language that can stop someone from being a sloppy coder.

It's actually the opposite. This would throw an exception in a strongly typed language. It would silently return the wrong thing in javascript because it's not strongly typed.
The point of the argument is catching errors at compile-time. Typescript doesn't make (and can't make) any guarantees at runtime.
In case it wasn't clear, that example compiles without error, whereas a more strict compiler would complain that a any is not a Thing, or some similar error.

The code doesn't even throw runtime errors, but you wouldn't be able to tell something is wrong unless a) a request providing the json string actually responded with the error string in the example (an edge case that is very easy to overlook during regular development) and you managed to catch the problem in tests, or b) you specifically went digging into the file that defines getThing looking for the implicit any cast that the compiler didn't tell you about or c) there's a compiler update that turns that any cast into an error

What others are saying is that Java and friends do c) (throw compilation errors when implicitly casting between incompatible types)

Java would absolutely stop you from returning a JSON object when you're supposed to return a Thing. You could cast it but you'd still get a ClassCastException. Now I haven't written JS or TS, so maybe I'm not understanding what you're saying?
Just like Generics in Java, Typescript only exists at compile time. Talking about what Typescript doesn't do at runtime is moot.
Typescript permits you to write code that does implicit unsafe casts at runtime. Type-safe languages don't. Java Generics don't let you implicitly do unsafe casts[1]; erased generics mean the unsafe cast wouldn't necessarily fail visibly at runtime (though there are warnings and linters to alert you to that, and techniques to work around it), but it would enforce that it was explicit in the code (and you would then know to add extra test coverage etc.).

[1] You can implicitly do unsafe casts using arrays, but arrays aren't really using generics, they're a special-case builtin.

The example above is equivalent to gson.toJSON(string, Thing.class)

It'll just return an empty Thing right? No ClassCastException.

No, those languages actually enforce type checking at runtime. The libraries for parsing JS that support parsing into a custom type will blow up right there if the provided JSON cannot be parsed into the provided class, not 5 steps later when you try to use something from that class that isn't actually there.
If you're not using a bunch of generics, check out typescript-is [1]. It takes little work to get it setup, but it generates run time type checks for you. I understand why typescript decided to not add this functionality to the core of the language, but it's starting to feel like the largest missing piece of typescript is a built-in way to generate run-time type-checks for user-defined types from just the type definition.

The happy medium we've found with that module is using the runtime type-check on anything "unsafe" to bless the result using typescript-is's equals functionality, but still allowing programmers to use casting with a comment justifying its necessity. For us our list of unsafe is results pulled from the db, anything parsed from JSON, and incoming request bodies (which can be a special case of parsing from JSON, but not always).

[1]: https://github.com/woutervh-/typescript-is

Doing runtime-typechecks really is a blessing, and it would be great if typescript would provide this as a language-feature. There are two more libraries I know which are great for that:

* Zod: https://github.com/colinhacks/zod * io-ts: https://github.com/gcanti/io-ts (for the more functional-programming-oriented)

We decided against io-ts because we'd still have to write the validator ourself (unless we used their type system as the source of truth, which wouldn't easily work for our project because we generate most of our types from an openAPI schema), which is exactly what we wanted to avoid doing.

I hadn't heard of Zod before, but i really liked the "parse don't validate" blog post linked. Turns out that's exactly how we've been using typescript-is. But it still suffers from the problem of expecting to be the source of truth and thus not working well with generated types.

I like io-ts, but I've moved mostly to runtypes: https://github.com/pelotom/runtypes

I forget why Zod didn't pass muster for me now, but I've looked at it before and didn't find a compelling reason to jump to it. Thought it seemed good, though.

I really wish at least one of these let you emit JSON schema back out, though. That'd be way easier for dealing with stuff like OpenAPI.

We go the other way. We start with openAPI, use dtsgenerator to spit out typescript types, and then use a library (typescript-is) that will generate run-time type check function as part of compilation.

A downside to this is that you need to compile type changes before you can use the change in code, but it gives us a single source of truth for our type definitions and let's us generate a more exacting request and response verification functions that use the (sometimes) more precise open-api definitions (eg a an array with a maximum number of elements and specific string formats), and it's really easy to add a run-time type check to the result of calling into a 3rd party library as well.

For runtypes, you could write a script to use its Static function to spit out the interfaces in pure typescript and then use something like typeconv or ts-to-openapi to make your openapi definition, and then use that script inside your build script to output the openapi schema.

Interesting! How do you wire it into validation, etc. for your web layer?

If you've got an example I'd love to see it. I've long been not a fan of OAS-first, but I'm always down to learn something new.

We like OpenAPI first because it's more exact than pure typescript types. I suppose there's libraries we could use to give us a similar level of exactness that would also get us typescript types and OpenAPI with similar exactness to openAPI first, but then that's another dependency that's not giving us anythign extra compared to openAPI first.

So the way we do it is a little meh because we have two separate run-time type validators, but it's because of the path we took where we adopted request and response validation almost from day 1, and other runtime validation came much later.

We're using express and express-openapi as the base of our project. Express-openapi has built-in request validation and type-coercion, and an example of how to quickly bolt on your own response validation, so for request and response validation we're using that. And it works. It's damn solid. But the docs for how to use the underlying tech to do similar run-time validation for say pulling values out of the db is lacking and I don't have enough expertise in the underlying tech to stand it up quickly, especially compared just dropping in typescript-is changing tsc to ttsc and adding an tsc plugin to our tsconfig.

If I were to do it again I'd either bite the bullet and spend the time figuring out how to get express-openapi's request validation infra to also do general type validation on just the schema, or i wouldn't use their run-time validation at all and just make my own middleware to do it, just so that I've only got a single run-time validation library. The nice thing about express-openapi is that since it "knows" the open-api definitions it can programmatically apply the types needed for validation, so rolling my own would require that functionality, because I don't want our team to have to remember to add the same middleware to every endpoint using a factory function that takes the typescript-is validator function as an argument, and the nice thing about typescript-is is that i can give it a typescript type and get a validator function exactly where i want it with no muss.

Anyway, our build looks like this: 1) compile typescript 2) compile the openAPI to a single file (we put the endpoint definitions in the endpoint file) using express-openapi (just initialize and then write out the JSON.stringify of the apiDoc field) 3) run dtsgen against the output of step 2, and pipe that through `sed 's/declare//g' | sed 's/namespace/export namespace/g'` and into some file.ts 4) compile the file from step 3 with typescript

step 1 gives us typescript compiled to js step 2 gives us a single openAPI.json file we can pass around or import into postman step 3 gives us all the types from openAPI in a ts file with a bunch of recursive exported namespaces step 4 gives us those types in a .d.ts file

We do 3 and 4 in a subdirectory so that we can publish the types as an internal package, which means we can give front-end a types package to consume for all the calls we want to make to backend. Well, we actually publish an sdk package that wraps the types and encapsulates the logic for making the HTTP request to the right endpoint, so we don't have to worry about calling POST when the endpoint is PUT or fat-fingering the response type cast through the entire front-end and can instead just call a function with well-defined types. Plus, now it's super easy to test the interface, because we have a single thing we need to test. So we write an exhaustive happy-path (ie 2xx status codes) integration test against the sdk that actually calls into backend gets the response and checks the response against the type and then the values we expect for each field.

Just a quick tip that helped my team with such issues:

- For every call to an API, or input from the user, there should be a few tests checking for unexpected data

This rule alone forces us to fix the majority of runtime errors such as this example (since JSON.parse() is usually called when handling an API response)

You're not wrong. That's a good example of why someone might have beef with typescript. But as others have been saying, tooling enabling faster dev is one of the main reasons people use typescript. Safety comes second for most folks. And the tooling works just fine in that example.
This particular example is close to my heart: the definition files for things like JSON.parse() were written before the "unknown" type was introduced as an alternative to "any"

And because this "any" is coming from an external definition file, it can't be caught by tsc's --noImplicitAny nor eslint's no-explicit-any

Those two rules allow us to protect us from our own code, but not external definitions/libraries. Relevant GitHub issue here — https://github.com/microsoft/TypeScript/issues/26188

Looks like eslint is working on a new set of rules that would help with these currently-unprotected any cases: https://github.com/typescript-eslint/typescript-eslint/issue...

> Create a rule which uses type information to determine when you're inadvertently breaking type safety by using an any, potentially without knowing.

> Often libraries (or even the typescript defs themselves) can have weak(/lazy) types which return any. If you're not careful, you can inadvertently introduce anys within your codebase, leading to bugs that aren't caught by the compiler.

> (e.g. people wasting hours trying to write a "function" that takes an enum and outputs another enum w/ a fixed prefix...

Metaprogramming shouldn't suck so much that this takes hours.

Sure, but you also should never just blindly trust a JSON.parse, and if possible you should avoid typing functions for exactly this reason, and let their return types be implied instead. If you didn't type `getThing` then its return type would be `any` and you'd get an error in your IDE on the last line.
sorry but i feel like this is backwards. the error - incorrectly assuming an `any` will be a `Thing` - is in `getThing`, and that's where the error message should be. getting an error in code that calls `getThing` instead would just obscure the real issue.

also, i think "avoid typing functions [...] and let their return types be implied" is terrible advice. argument and return types are the thing you should type explicitly, because it makes the errors local. otherwise you'll get all the fun of tracking down where that `null has no property "blah"` error came from, just at compile time

If you don't annotate the return value you open yourself up to another class of mistakes because the compiler can't check that each branch returns the correct type, especially if type inference decides the function returns `any` which swallows `undefined` in type hints, while also losing the self-documenting aspect without an IDE.
Check out io-ts — it can take a minute if you’re not familiar with functional programming, it’s built on top of fp-ts.

Being able to encode/decode IO safely has been a huge blessing when working with JSON.

This is the one thing that bothers me about TypeScript as well. I've ended up using frameworks like `zod` to do runtime type checking of endpoints. It has gone a long way towards decreasing edge case failures -- I just wish they had something like this built into the language.
To me, the solution is simple: use a single source (some IDL format) to define objects at API boundaries and have your frontend and backend use it.

It will make it a lot harder to return wrong types from your calls. Personally, I like gRPC + Protobuf.

Wait... so I assume `JSON.parse()` returns `any`, yeah? TS automatically downcasts (in function returns and assignment)? How is that at all useful?
It's a historical artifact of Typescript's development. The `unknown` type wasn't introduced until Typescript 3.0 and since `JSON.parse` is part of the Javascript "standard library," changing it now would be a brutal breaking change across almost the entire ecosystem.
I've always told folks that no technology will save you from your own developers.

A good application can be written in any technology, just like a bad application can be written in any technology.

Certain technologies will try to guide you to a better place, but it's up to developers to take advantage of it. Or to do crazy stuff like the above. . .

The problem in your example isn't TypeScript. It's the JSON parser. If you used GSON in Java, you'd get the same result, despite being statically typed with runtime type checks.

You can implement the JSON parser to be type safe. For example, this is how nest-dto can prevent this.

As a "senior" I'm still not sold. Running across bugs in the type checking that blocked us for a while (trying to avoid Broken Windows to boot) and the fact our development times skyrocketed (including on personal projects) or that the answer to poor tooling was "just use VSCode" is not something that really screams "developer friendly".
Sure, like all systems, there are overhead costs involved. But with something like a typed system, the overhead costs are minimal compared to the benefits you reap from them.

One of the projects I worked on during my transition to typescript was to convert an existing project into it. Sure there was a lot of head banging against walls at some point to get the types to work out, but during that process I finally really understood how everything fit together and gained a sense of assurance that I could use a return of a function with complete confidence. Developing on that project afterwards was also a breeze, moving faster and more confidently knowing that the object types were set, and that guardrails were in place to prevent myself from shooting my own foot.

It's a new system to work within the guidelines of, but guardrails are there for a reason, to prevent you from falling off a cliff.

You are either going to catch the bugs in the type checker or you are going to have to write unit tests to catch them.

For me, typed python saves me a bunch of time.

Typed python takes ages to validate, and Python is much slower to run than Node.js, even with hacks like uvloop.

I've barked up that tree so many times, I don't think I'll ever go back to Python for that stuff in the future.

The problem is, Javascript was never meant to be typed. It was never meant for what we use it for, even. When you start adding guard-rails to a language that already has guard-rails, what are you left with?

We spent more time screwing around with our Typescript types, getting the compiler to be happy, working around bugs with type assertions, etc. than we did focusing on the app logic at some points.

This kind of stuff, to compare, never really happens with C++. The type system is simple enough (compared to TS) that you can pretty confidently fix type errors quickly. I don't like Rust, but I know that the same occurs with Rust's type errors, too. Why is TS so difficult?

Because Microsoft created a type system that is so complex it can represent (almost) any type in a dynamically typed language. It's impressive, but not at all practical.

Senior here. I was afraid if typescript will follow C# or Java's type checking. Both are too strict, verbose and inflexible.

I was afraid to introduce complex generic types because it isn't easily represented in typescript, or to change a struct from "person" to "employee" will need to map each properties individually.

Thankfully that wasn't the case.

I can't speak for the author, but I can speak from a similar path. Speaking for myself, I was self taught, and to even have a toehold in any kind of career I had to learn to prioritize advancement. Not having an educational or theoretical background, that meant evaluating "what seems most effective right now". I was late to adopt TDD, late to static types, late to FP, late to a zillion things... because I was prioritizing not being sent back to broke/poverty life. Once I had enough air to breathe I realized all of these things help serve me staying out of that misery, but the urgency of learning in the field doesn't always give room for that analysis.
(comment deleted)
For me, I can handle the lack of static typing when I'm doing small scripts, like if it's going to be 100 lines or so of Python (my go-to language if I need to test some math out in a sandbox or do some file manipulation on my system). I might actually be a little faster in writing those scripts because of the lack of typing.

Once it starts getting any bigger than that, though, static typing really catches and points out a lot of problems before I even launch the program, that I'd otherwise be spending time debugging and looking for anyway, most likely.

I also think static typing has the benefit of enforced self-documentation a bit, so other developers know just by checking the type what sort of data they should expect to see in there, and not trying to give it garbage to begin with. Granted good variable naming and proper documentation would alleviate that, but that's never been fully consistent at any organization I've ever worked for.

And considering I mostly work with relational SQL databases, there's no escaping typing on the database end for that, so I might as well have models that map to the typing of what's in the database so I don't have to keep checking the database tables to make sure I'm giving it good data.

Naming is incredibly hard, though. And refactoring is hard.

It's a pain in the neck when something starts out as bla_list or whatever and you discover that after half a decade of changes bla_list isn't actually a list anymore, it's a dictionary, and that it doesn't only hold Bla these days, it can also contain Foo or Bar. This type of stuff is immediately obvious with modern static typing.

I agree, naming can be incredibly hard. I periodically spend a long time trying to decide the best name for variables, and at times whatever I come up with isn't satisfactory to me even then (and I usually try to explain it better in a comment at its declaration).

I don't always change the names if their use drifts a bit either because I know the other developers might be used to the code where it's named something else and it might confuse them, and/or I have to rename it in a bunch of places that might make a merge particularly messy, especially if I'm working in the same place that I think another dev might be working on at the same time (it's happened more often than you'd expect actually, I've had to do some particularly gnarly merges at times).

This feels more like developers learning that any variation on Hungarian notation is a bad idea.
You have to account for developers learning, for any code base, though. Let alone for third party dependencies :-)
This feels less like a case of Hungarian Notation being a bad idea and more like a case where developers are making irresponsible updates to their code. If a variable/function/class/whatever is being updated, the naming should be updated as well, if appropriate.

Blaming the original author for the actions of the maintainer is not fair.

I'd still blame the individual developer for poor naming. Naming a variable something like int_list is terrible in that it's both very specific and semantically meaningless. Either choose a semantic name like grades or part_numbers, or take a hint from ML convention and use semantically void names like "xs".
Sure, but is there anything stopping the person updating from revising the name? The original quality of the name is not relevant to my point, which is that anyone changing the code but leaving poor names behind is complicit in the debt.
I guess static first is a chore because at that point you have no idea what invariant or structure is required for the task. After prototyping you know where to tighten the bolts.
There are some languages that give you a bit of both worlds, like F#.
This! My favourite thing about working with F# is the tiny delta between prototype script and production MVP.
hey, the ml family is only 40+ years old, maybe in 50 years they'll be mainstream
Sort of but at the same time being forced to think about those things also helps get a handle on exactly those parts of the program. Remember you still need to think about them in a dynamically typed language but it's obfuscated because they're implicit.
> you have no idea what invariant or structure is required for the task

I often write the program in TypeScript types/interfaces first because it forces me to think about the data flow both in and out. In my opinion, data, not code, is the most important thing about a program.

that way of working was my only pleasurable experience in eclipse/java

start with interfaces and let the live type checker tell you where your incoherences are. Very very smooth way to work.

> you have no idea what invariant or structure is required for the task

In my experience, in general, if you don't already have an idea, you should probably be spending the up-front time thinking about it. If we're talking React/Redux, then carefully thinking through both data representation and what goes into components' properties/states helps a lot for designing a clean and understandable structure.

Designing my types first is precisely how I want to begin prototyping.
There is why TypeScript IS so great.

In the early days of a new app or feature, you have no idea what the correct data/type shapes are.

In TypeScript you can define the types and then easily do major refactors even if you have 1000's of lines already written.

It becomes so tedious to do the same refactoring in JavaScript.

TypeScript makes it trivial to change an object or function signature when developing new code (or old code).

Refactors that take 3 minutes in TypeScript take 3 days in JavaScript and you better hope you catch all the uses.

The refactor ability that TypeScript gives you makes it so much easier to change data types and function signatures dozens of times until you get things right.

SQL is not typed, it is just text sent over a network or whatever and then parsed...
The schema and functions are typed.
Seems like an easy cutoff is when you add a build step as opposed to just including your script on the page. If you need to use Webpack for a project, you might as well add TypeScript as well.
> I also think static typing has the benefit of enforced self-documentation a bit, so other developers know just by checking the type what sort of data they should expect to see in there, and not trying to give it garbage to begin with. Granted good variable naming and proper documentation would alleviate that, but that's never been fully consistent at any organization I've ever worked for.

I don’t think naming is fully alleviated by static typing; however, on the other hand, static typing allows IDEs to offer better refactoring capabilities, since they can presumably build such functionality on top of the compiler’s structured syntax tree representations.

As an example, lately I fairly frequently find myself using VS Code capabilities to rename TS identifiers as my code evolves. It always handles correctly the scope and everything, I’m sure it’d be more error-prone with plain JS.

Agreed about statically typed code being more self-documenting in general. It’s a bit of a double-edged sword in that it may give an illusion that your project is easier to understand than it actually is, but it usefully shifts the focus of documentation from minute details of code itself to subject domain specifics and how you represent them in your architecture.

I use type annotations in Python no matter what. It really helps skim the code faster and mentally understand what values I am expecting. Its very useful to know which var is a List[dict] and so on.

I don't like TS because javascript is just a silly language to do anything serious with (besides front end), and its so stringent it takes forever to write quickly.

I think a large part of the support for Typescript comes from being able to create good tools for the language.

But, I still have an aversion to front-end development itself, because it just feels too _involved_. You have to set up so much, and it has become a lot more difficult since you need to install webpack, postcss, and many other plugins just to do a hello world app.

It's still bearable if you have to do all of that, and you _understand_ what is going on, but most of the time, especially when it comes to front-end, I do things "because that's how it's done".

This makes me feel very insecure about my code.

This argument also holds for backend development. I've spent days trying to get a big Java app working with Jboss at a new place, it was more or less a rite of passage when joining the team. Also setting up some local obscure Microsoft IIS setting because reasons.

These days I find frontend tooling quite delightful, Typescript for instance Just Work(tm) our the box.

I can't comment on this, since I haven't really worked with JBoss, and haven't had to touch the .net stack in ages.

The python stack is pretty okay though. You just containerize it, and ship it. Some providers will even allow you to run containers as a function/lambda.

> You have to set up so much

I think tooling has mostly addressed this, or at least provided a mostly braindead path for 90% of needs. For example, npx create-react-app $YOURAPPNAME sets up practically everything you need to get a reasonable react app going, at least in terms of the transpiler/set of plugins. It makes very few choices re: actual react libs though (no preferred router, flux, etc). The preact equivalents are somewhat more opinionated, and go a bit farther in setting up routing/splitting nicely for you.

> This makes me feel very insecure about my code.

I get this. While I stand by what I mentioned above, it also makes me nervous that I understand so little of my setup given how much of it was setup automatically for me, which makes me trust the environment less. FWIW, outside of web dev it seems like _most_ people have no idea how their setup works re: compiling a project. How many devs for say postgres understand the make file or configure scripts? Probably not that many. Maybe that's getting better with newer tools like cargo, but web dev always seemed like the exception wrt understanding the bundling environment, and we're just now catching up to the level of complexity that other types of dev have had for decades.

Edit: apparently that was a bad example. Postgres's make file is very simple: https://github.com/postgres/postgres/blob/master/Makefile

The configure script is less so: https://github.com/postgres/postgres/blob/master/configure

That's not the real Makefile, that would be the GNUMakefile. Also pretty simple, though.
I've spent the last two years writing my own framework because I hated React so damn much.

It really doesn't have to be that complicated. Granted, I still use a bundler, but it's really easy to set up. I had my friend walk through it all... he seems to think it's great.

I should probably release this project some day.

Why did you hate react? It‘s really one of the less complicated frameworks out there.
It doesn't use native elements, and instead goes from React Components -> HTML Text -> Native Elements. So then the moment you have two elements interacting it falls on its face. Like forms.
Why do you think that? Forms can be easily manipulated with refs. A ref points to the native element. It‘s really not that hard
Really struggling to parse what you've said here
Any reason you forged your own versus adopting Svelte or Alpine? As a fellow React hater I'd love your take on those lighter frameworks.
It started off as just a shorthand tool for creating elements:

``` div("ClassName", { childNodes: [], etc: {} }); ```

Then it grew into having a state management system. And now it's easily the best framework I've ever used... super fast, light weight, extremely malleable, easy to understand.

I keep intending to release it, but I know just how much fun it is putting something out in the world... I don't want this project to turn into my life.

"First I didn't like it because it takes longer to learn, then I discovered the obvious features that are the reason TypeScript was invented in the first place."
Don't want to oversimplify since the article has some useful insights, but overall it reads like a typical transition from thinking static typing is a burden to realizing type safety can be incredibly useful.

I distinctly remember that in college I believed the history of programming languages is purely evolutionary in the sense that newer languages are objectively better than older ones. As in - people wrote assembly until C came along and gave them if clauses and for loops and then came Java where you didn't need to manually manage memory anymore and could use classes and then with Python you didn't even need to specify types anymore!

I.e. the newest language with the most everything is the best!

Nowadays it feels like I'm replaying that in reverse - doing less and less OOP, loving statically typed languages, wanting to learn some Rust etc.

If you're anything like me, once you've swung back in the other direction for a while you'll synthesise and realise that engineering is choosing trade offs and that every tool has an area where it shines - so its worth applying that tool to that area and not treating every problem like a nail to be hammered.
Of the languages you listed, Java is the new kid on the block. Python is older than Java.
Interesting, I didn't know that! Guess I still have some of my youngster naivite :). But that's exactly how I felt - I remember the introductory programming courses being in Java, there was talk about switching to Python, but it only happened a few years after I was a student. Same with companies - Java was for "old corpos" and Python for "hip startups".
> but overall it reads like a typical transition from thinking static typing is a burden to realizing type safety can be incredibly useful.

I got this impression too, I was just like the Author in that I hated static type checking as a Junior, but as I gained more experience it flipped from "I hate this" to "this is the first thing I implement in a new project".

To your point of thinking that newer languages being "better" than older ones. I think that also comes down to experience. At first I thought the same way, but the most experience I gained the more I realized that each language is a specialized tool for using in specific cases, some tools are very very specialized (WASM for example) while others are broad (like Javascript).

Not long ago I used to hate Javascript, and it was because I did not understand it very well, and then I read Flanagan's book "Javascript the definitive guide" and now I like it!

So, is there a good book that some of you would recommend, that would have the same impact as Flanagan's, but about TypeScript?

Are there big companies still doing untyped JS at scale out there?

I'm not a frontend person and I always disliked dynamic type systems. But I vaguely remember the skepticism when Angular moved to TS. To me it made a lot of sense that most projects at Google, MS and the like would hugely benefit from a language that is kind of a C# on the JS runtime. And in 2021 I see TS pretty much everywhere, but maybe I'm missing something?

In my team people object to TS due to a few things: additional complexity, additional build step, build time increase, copy-pasting code around (to console, code snippets etc.) does not work anymore (but last time we talked it was soon after coffeescript -> esmodules migration and I guess everyone was tired of coffeescript which might have affected the mood).

There's no unanimous consensus that TS is all gold; some people who are fine with types don't like certain TS implementation details and in fact some devs start saying out loud that sometimes it's very difficult to fight TS with certain constructs.

Something that's gaining popularity and what I'm trying lately is using JS + jsdoc annotations more thoroughly, which gives the niceties of TS (when using vscode) without the downsides and heavy compilation step:

https://www.typescriptlang.org/docs/handbook/jsdoc-supported...

I loathed typescript at first because of the heavy compilation step, but setting it up to compile only when it was necessary (building for production), and to transpile the rest of the time (running tests, starting a server locally) made a huge difference for our team. A slow compilation step really is a huge cost!
While gaining popularity, JS + JSDoc is misguided and not a solution. The compiler is what is required to actually enforce standards within a team - otherwise what you have is a completely informal process.

I don't think type docs are useless - on the contrary, they are super helpful while migrating. But I think this trend is indicative of a different issue - what we are really seeing is more of a protest that JS needs native optional type support as soon as possible.

> Something that's gaining popularity and what I'm trying lately is using JS + jsdoc annotations more thoroughly, which gives the niceties of TS (when using vscode) without the downsides and heavy compilation step

I'm a big fan of this as well. Webstorm also supports it.

It feels like the right balance to me, as it gives me a heads up when things don't match, but doesn't require that I go through the tedium that TS imposes once it touches a project.

Now I just need a guide to solve my anti-javascript and I can travel freely through all abstraction levels.
After being an early adopter of TypeScript, I decided to skip TypeScript in my next projects for a few years because I thought it was not worth the hassle. I remember spending a lot of time in DefinitelyTyped to create and fix type definitions, and when Babel and ES6 were popular I used that instead.

I recently changed my mind and started to migrate a large JavaScript codebase to TypeScript, in strict mode. I consider my team to be composed of only good developers but still, I found so many bugs. I still spend time on DefinitelyTyped definitions, but if you do more than prototyping or work on a project also developed with other people for more than a few weeks, TypeScript is for sure worth it in my opinion.

Typescript is more popular now because projects are migrating more and more business logic layer to front end. Thus the need for type safety and static analysis.

I’m agnostic about Typescript. Bare JavaScript is very efficient and optimized language, well suited for smaller projects.

However, when project scope balloons up, then Typescript becomes more useful. It’s way of enforcing descriptive comments a code, than trying to be a type safe language.

Although I like the actual language of TypeScript more than JavaScript, it still doesn't feel useful enough to justify its usage. Computers' native language is Assembly but we rarely use it and prefer to code in C/Python/whatever because this is so much easier (+ portable). Browsers' native language is JavaScript and it isn't much harder nor less powerful than TypeScript, nor does it provide a seriously different perspective (like Clojure/Elm/Reason do) so why not just use JavaScript (you have to understand it anyway)?
The value of TypeScript is a function of the codebase size, size of team, and length of time for maintaining this codebase.

If you're an individual, putting together dinky little web sites that you don't need to maintain for years - the value of TypeScript over JavaScript isn't as pronounced.

If you have a web application that counts hundreds of thousands of lines of code, maintained by team of developers over many years - JavaScript does not cut it ... at all.

This is incorrect. This is just repeating popular rhetoric.

Well designed projects are broken up into manageable, individually testable modules... So codebase size is an anti-pattern to begin with. As for time, you think waiting 15 additional seconds each time you want to test something doesn't add up and waste time? What about having to type up all these extra type definitions? You could have spent that time writing more test cases.

>Well designed projects are broken up into manageable, individually testable modules

And JavaScript does not make it easy to do that. It also makes it very easy to do it incorrectly and in a way that is an unmaintainable mess. Every programming language can be used to write clean code if you try really hard, but not every programming language will have the syntax, semantics and conventions that will make it harder to do the wrong thing.

If you do try modularize JavaScript, you're going to need a compilation chain to package it all together - so you're not getting away from that.

>So codebase size is an anti-pattern to begin with.

No it's not. What are you talking about? A large application will have a large code-base. You can't get away from that.

>As for time, you think waiting 15 additional seconds each time you want to test something doesn't add up and waste time?

Sure it adds up, but it's a tradeoff. If you're writing a dinky little web app for your grandma, maybe that trade-off isn't worth it. If your codebase numbers in hundreds of thousands of lines of code then you take the hit with compilation so that you can save time in other places. For example, tracking runtime bugs that a compiler would catch, is very expensive, and gets more expensive as the application grows.

>What about having to type up all these extra type definitions?

Heh. That's not a real issue. Programming isn't about speed typing and measuring 'words per minute'. You're not losing time if you need to write 'int i' vs 'var i'. Again, we're not talking about writing quick scripts or throwaway code. We're talking about code that may need to be maintained for years by many different people.

>> If you do try modularize JavaScript, you're going to need a compilation chain to package it all together - so you're not getting away from that.

That's not always the case on the front end (it's still possible to code on the front end without a bundler). And it's certainly not the case on the backend (I.e. Node.js).

>> Sure it adds up, but it's a tradeoff. If you're writing a dinky little web app for your grandma, maybe that trade-off isn't worth it.

This could not be further from my own experience having gone back and forth between JavaScript and TypeScript multiple times both professionally and as part of my open source work for over a decade.

I've built highly complex scalable distributed pub/sub systems, P2P systems, blockchains and a decentralized exchange using plain JavaScript in record time with no bugs found for over a year.

I just can't bring myself to use curly bracket languages. I can't make a good enough case for any of them.

I read way too much code to make myself read code that's not beautiful.

Do you use a different solution for frontend web development, or is frontend web development a domain you get to avoid?
what are alternatives?

python's tabs?

The major choices that avoid braces are indentation and keywords.

Indentation's upsides are less line noise. In my opion, while there's less noise, there's less symmetry, so it's a wash on the beauty count.

Indentation breaks many tools, especially the way amateur coders use them. They're frequently showing code snippets by copy-pasting into web pages that strip indentation.

Notably, the standard pylint rules insist on two lines between functions and classes because the extra whitespace helps people find things. That same whitespace you get if you close a function with a single brace...

Keywords are surprisingly nice. Lua does them very well:

    function foo()
        doSomeStuff
    end
You get rid of the pointless { at function, and you have a simple "end" when you're done.
that's an odd take considering the article is comparing JS/TS which have an identical amount of curly brackets
>> satisfying the TypeScript compiler is an over-engineering and not providing any meaningful benefits

I tend to rail quite hard against the “type everything” world view but I’ve always been crystal clear on the benefits of types at boundaries between components. They can be a succinct way to codify the invariants and contract between components in a software architecture - with a nice quick feedback loop when you appreciate it most (when you’re exploring an unfamiliar API). They can also be a total train wreck for productivity when abused.

Where they fall down for me and always become a productivity penalty is working inside those component boundaries. Tests are a better answer then. I care that the actual behaviour is sound, if i say concat(list1, list2); something like Java’s type system guarantees of only “hey you put two lists in, you get some list back which might or might not be a concatenation of the two you provided” aren’t sufficient, you might as well not type it at all. Tests are necessary.

In typescript you could actually type concat in a meaningful way but i digress.

I think tests are also a better tool for handling people boundaries, that is developers separated by time (i.e. myself in 1 year) or separated by space, (i.e. between groups of developers in different orgs). The fundamental difference is the explanation, a test explains how something should behave, an intention. A type is a terse expression of how a person interpreted the problem domain and is not guaranteed to be an appropriate or useful model, it’s just a rule.

There’s a possible exception to all of this for me - dependant types. I’m still learning and undecided on the value.

All said and done, I’m happy that things like mypy and typescript exist, but I’m not convinced they are a cost effective way to maintain reliable software if over-used or used in rubbish ways. Used appropriately, they’re great. Used all the time, no thanks.

Yep, I'm a firm believer in using TS pragmatically. Type the obvious code (React component props, API responses, function arguments), and start by aiming for an "80% sweet spot" of type coverage. Don't get into arguments with the compiler, and avoid writing really complex types unless you've got a key spot in your code where adding that type now makes it a lot easier to infer types in other places:

https://blog.isquaredsoftware.com/2019/11/blogged-answers-le...

While I'm fan of statically typing, there is something with Typescript I don't like, but don't know exactly what it is. In my day-to-day it's mostly Kotlin and Elm, which also are both statically typed, which I don't mind and mostly don't even notice.

But with Typescript it is as if I'm always fighting the compilator, trying to make it happy with whatever code I wrote. Maybe it's just the JS ecosystem being too lax that makes having types for everything difficult? Like you have a react hook and some components that you can compose, and the typedefs just become unwieldy and impossible to get right. Often they end up not even catching what should be compilation errors.

> Like you have a react hook and some components that you can compose, and the typedefs just become unwieldy and impossible to get right. Often they end up not even catching what should be compilation errors.

I’m curious - how do Elm and Kotlin solve these problems in a way that balances the often competing goals of “add types to reduce errors” vs “have a high productivity environment where it’s quick and easy for engineers to write new code”?

Elm and other ML-family languages have type inference, it does not ask a major part of human annoation of types while catches errors in compile time nonethelessly
Kotlin has type inference as well so typically you only write types in function signatures. It's rare that you write a type in a function body. Coupled with good IDE support for showing you the type of x in `var x = myFunc()` when you need it.
Basically in the same boat, although I still use TS as it's become kind of defacto in JS land, I'm not sure that I dislike it so much as am not sure it's always worth it. Maybe I tend to approach it too much like Swift or Kotlin and get annoyed when a dependency is missing typings or the compiler is so easily tricked/lax about a lot of the actual type-saftey.

Maybe putting Typescript it into the bucket of a "better linter" is the mindset to have.

I had this too - when I first started I fought typescript quite a bit. I struggled through this because I kept finding more and more code on github that was TS, kept seeing more articles that were TS, kept seeing big improvements from MS on TS, etc. So I kept learning the best practices and trying to understand why I shouldn't use "any[]" or "{key: string?}" and so on - trying to get past the parts where I wanted to be lazy. Eventually on one of my projects I had to reorganize my model structure, where some classes held other classes and so on. I had to both move one and rename one. This was early on, but since I did a reasonable job with typescript, I was able to just run `tsc -p .` over and over and it found all of the places I missed, which were far more than I would have guessed. It only took maybe 20 minutes to do that massive refactor. From that day forward, I really enjoy working in typescript. I think it's different from (some) other strongly/statically typed languages because it allows you be be a little more expressive in what you're defining. Perhaps expressive is the wrong word, but I find that it's quite flexible in this area, and I really enjoy that because if I take the time to flesh things out it can really help me find or prevent bugs.
TS has a bit of Java feel to me. Forces me to follow all kinds of hard compiler requirements that take some time to implement, like generics, but then there's still all of these edge cases like null-pointers that make it feel like you're still building a house of cards.

Kotlin and Elm are more functional, don't know about Elm but Kotlin has non-null pointers as a base feature of it's language, not some later bolted on tie-wrapped solution.

I like the post, but the author's original perspectives are something I was always perplexed by. There was a period in late 2000s and early 2010s where dynamic languages were all the range (Ruby, Python, CoffeScript) and static typic was seen as archaic and slow by an entire segment of development community and I never understood it.

>I always felt that adding types to the functions/variables and satisfying the TypeScript compiler is an over-engineering and not providing any meaningful benefits.

I mean ... that is a perspective. EJB 2.0 was over-engineered for many use-cases. SOAP is an over-engineered standard for many use-cases. Static typing is not. Maybe for small scripts, static typing doesn't provide that much value (although even then I don't really buy it - why wouldn't you want to know what you're working with at any given time), but for sizable (web) applications that need to be maintained for years, it's critical.

>also it felt slow to work on, as I always used to get some compilation errors which were hard to understand initially, and I scratched my head trying to figure out the problem.

Another interesting perspective. Every single one of those compilation errors was a runtime error waiting to bite you in the butt. If you think solving compilation errors is slow, debugging and solving runtime errors is way slower. I'm glad the author learned the value of compile-time checks.

>The other reason was Advanced TypeScript concepts like Generics felt very hard to understand initially and I started feeling that I am in the Java world where every piece of code is strongly typed and overwhelming.

This right here is the big problem in a nutshell. There is nothing wrong with ignorance - we all start there. But it's one thing to be 'overwhelmed' by something when starting out, and another to being ANTI that something BECAUSE you don't understand it. The author admits he was overwhelmed by TypeScript and that caused him to be ANTI-Typescript. That is something you see in places. And that the author justified his Anti-Typescript perspective by having an ignorant view of Java to boot. The author talks about 'Java world', but does he have any experience with Java development at all?

Not all type systems are created equal. I could see how someone whose only typed language was C++ could think that it’s a waste of time.
> Maybe for small scripts, static typing doesn't provide that much value (although even then I don't really buy it - why wouldn't you want to know what you're working with at any given time), but for sizable (web) applications that need to be maintained for years, it's critical.

It's common for web devs to spend a lot of time dealing with semi-documented lumps of JSON. If you don't have any guarantees about the data in the first place then static typing ends up being a lot of pain without much benefit.

Sure, the situation is far from ideal. But when you're in that situation static typing isn't solving your first problems.

>If you don't have any guarantees about the data in the first place then static typing ends up being a lot of pain without much benefit.

Uh huh. Are you trying to come up with scenarios that you think a typed language wouldn't be useful ... because you don't understand typed languages?

If you have to work with crap JSON blobs with arbitrary and inconsistent typing, static typing doesn't prevent you from treating it as a 'dynamic' untyped map. That's true of pretty much every typed language there is, from TypeScript, to Dart, to Java, to C#. I do a lot of JSON consuming in Java and Dart. My preference is to enforce a schema on the incoming JSON blob and parse it into a typed object automatically, but if I can't, then I just parse it to Map<?,?> OR List<?> and work on that level.

At some point you're going to have to write code to figure out the type of whatever property you're interested in because it may be important to know whether the property is a string, an int, a float, an array, or whatever.

>But when you're in that situation static typing isn't solving your first problems.

Neither is dynamic typing. You're in the exact same boat in that case ... except you get the benefit of typing in the rest of your app.

I have noticed a lot of people who grew up only using dynamic typing languages are discovering static typing for the first time and proclaiming it to be the "new way forward" or something. Kids...
What exact value does this comment add other than some weird attempt to give off some air of superiority?

A lot of of people are "discovering" TypeScript because it is the first widely used and well supported attempt to bring static typing to a language that is currently the only mainstream option for programming to web.

They mean that people who have been back and forth multiple times over many years don't see the value in TypeScript. This is totally consistent with my 15 years of experience working on many different complex projects and having gone back and forth between JS and TS.

TS helps if the project is really poorly written... But that's not something to aspire to.

I have 15 years experience and I agree with you completely. The TS propaganda is so intense that I'm seriously wondering if Microsoft is paying people to shill TypeScript... I just can't figure out why they would do this but the degree of misinformation and downvoting is so intense that I can't think of any other explanation.
Here's a project idea I'd really like to see: A version of Chromium, targeted at web developers, that is is modified to accept TypeScript syntax as a variant of JavaScript.

This hypothetical browser wouldn't perform type-checking, just be able to parse TS syntax and convert it to plain JS, in the same way tools like Babel do.

This would let you use TypeScript without the need for any transformation pipeline in dev. You could then separately run TypeScript in a console to get async checking. Then only when you build for production do you run everything through a compiler/bundler to get a plain JS bundle that will work in everyone's browsers.

That would be the way to get the JS ecosystem out of the mud. But it would be great if there was a way to ensure nil and type safety on the level of Kotlin, Swift, Rust etcetera and build from there. TS is more like Java in that aspect.
Typescript is boring, and boring is definitely something need to be treasured in the frontend world.
This article echos the common complaint I've seen from people hesitant to adopt TS.

"I always used to get some compilation errors which were hard to understand initially, and I scratched my head trying to figure out the problem. "

Which is like saying, "I got rid of my carbon monoxide detector because it kept beeping at me"

I kinda like what you are saying, but it's not that clear-cut in my experience / skill-level.

One can write code that would work correctly, but trying to express the types as precisely as one would like may be painful and doesn't necessarily be productive.

Sometimes when I feel it is cumbersome to express my intent in typing, I may feel satisfied enough to make a runtime check, (and perhaps add a note but depends...). Especially if that functionality is used in scoped area (e.g. module) and not exposed to the rest of the app.

When trying to make a generic and flexible functionality, the typing experience seems get harder and more time-consuming for human to grok, and it may not even be that useful when you look how the code is used.

So I usually try to make typing as good as I can by finding documentation related to what I'm trying to do and banging my head to the wall from a couple of angles, and for some specific parts I may give up and type any.

Perhaps sometimes when typing feels hard it may tell that the abstraction is just plain wrong, so there's that plus side!

I'm a senior developer and I've never touched TypeScript and don't have any desire to unless I have no choice.

I haven't worked on any projects that were large enough to warrant type checking or where I felt it would make a huge difference. Also I find it a bit verbose and ugly and I'm just not a fan of pseduo Javascript languages that need to be compiled down into JavaScript.

Same. Lead dev here.

Part of my problem stems from the lack of typed libraries across the JS ecosystem. Party supported typing leads to more issues, I've found.

Then there's the IDE. I don't use code hinting. In fact, I find it irritating. So, I draw no benefit from TS in this regard.

Then there's the cryptic error messages. They've gotten better over the years but I always seem to spend more time debugging TS than writing code.

And finally, I just don't run into the kinds of errors people talk about solving with TS.

That said, I did enjoy the strong typing that AS4 provided back in the day. But TS is intolerable to me and I've worked with it for years.

As another lead/senior dev, I've found that onboarding hires that know some TS into a TS codebase is a hugely slicker process than onboarding people into a pure JS codebase, simply because the typechecker can do the overhead of having to check each call etcetera.

The issues you are having sound like teething issues but as someone who's worked with TS for years I've found they aren't hugely difficult to overcome, either with @types packages, or writing declarations, or declaring module (and having an "unsafe" library that people use with care).

> I don't use code hinting. In fact, I find it irritating.

I mean that's a personal preference, but if you're a lead dev and you have people starting work on a codebase they have little experience with, it's a godsend because they aren't totally blind.

> I mean that's a personal preference, but if you're a lead dev and you have people starting work on a codebase they have little experience with, it's a godsend because they aren't totally blind.

Very much this. I pushed to move the scripting language for our game engine project dot big bang from JS to TS primarily because the typing makes the entire codebase much more discoverable and beginner friendly. Using Monaco embedded in the browser programmers get great auto-complete and inline code documentation as well as the compilation step making some simple and common runtime errors much harder or impossible to run into. Qualitatively the mixed experience bunch we have using it now all much prefer the TS experience to the JS one we had before.

> I haven't worked on any projects that were large enough to warrant type checking or where I felt it would make a huge difference.

Can I esk exactly how you're considered senior if you've not worked on any projects large enough to warrant type checking?

Same here. I still do not get it. I see the benefits of typed languages for use cases controlling binary data, like hardware controlling, drivers or binary file formats.

And actually you can write very type safe code in pure JavaScript.

1. (arg) => if (typeof arg !== "string) throw new Error()

Of course, this is executed during runtime (which could have benefits too sometimes) and not at transpile time.

Yes, you can laugh at me. But.. You should be always aware of the types you are using, which data is coming in and if the function is type critical, which is only the case for some functions, it is even more fault safe doing it this way, because your app is spitting an error during runtime, which will definitely be fixed very quickly. :) To validate data, use typed structs.

2. You need to write good tests anyway. Tests should test with malformed and faulty data always.

I and the teams I had, maintained and coded big JS applications. I can remember only one case, where TypeScript could have been helped with typing. But also, we had no test written for it, so..

I was hesitantly open to TS when the hype was first ramping up. The team I was on (and I've seen this in lots of code elsewhere too) was doing stuff like this:

  declare let myColor: Color | undefined;
Suddenly everything turned into Java with signatures cascading endlessly. I'm not blaming TS, it's more of a.. lack of faith to make the full jump? Or limitation of the underlying Javascript? This problem should be solved with having multiple function signatures, in the example above one with and one without the Color param.

I've been toying with Erlang for years, once I grokked how they flow control can be done via different method signatures, the Other Way started looking really inadequate.

This is my problem with TS. The underlying JavaScript peeks it's gross head through the cracks way too much. I end up not bothering with types since they're often wrong at runtime anyway.
If "| undefined" is spreading through your code like a virus in cases where it's not actually optional, then I'd argue that the problem is upstream where the data is ingested, and the code should be enforcing "must be defined" at input validation so that you can carry a clean "Color" type everywhere else.

If it's about optional parameter weirdness and TypeScript's lack of method overloading, I suspect that's inherited from JavaScript's laxness with method signatures. I don't like it either.

TypeScript does have method overloading same as other typed languages (except you have to do runtime checks, but that is a JavaScript limitation).
> except you have to do runtime checks, but that is a JavaScript limitation

IMO not having to do runtime checks is a big part of the value proposition of method overloading the way many languages do it. Whether or not it’s a limitation inherited from JavaScript, it’s still really klunky compared to other mainstream languages.

It’s one of the few things that I actually dislike about TypeScript, despite overall loving it.

assuming you meant

  let myColor: Color | undefined;
I don't understand what the problem is. Native JS would require you to define the starting scope of the variable (unless you're relying on global scope!).

Then you declare what the type is: Color or undefined. The state of the variable can be one of two things. Perhaps I've been writing TS for too long but I think I'm missing the problem here.

The problem is that if anything can be undefined, then you don't really have a big typing advantage over regular JavaScript.

It's just like being in Java land where typing adds a lot of boilerplate to the code, but you still have to do null checks everywhere.

That’s a problem with your codebase, not with TS.
That's the crux of this entire debate.

The benefit of a type system is that it provides an organizational framework for code bases that need it (i.e. multi year projects involving a rotating set of developers). That's the only argument for a type system that I agree with (I don't believe it reduces bugs or anything like that, and from what I've seen there are zero studies that conclude such nonsense... this is a 40+ year debate we're talking about).

Structural type systems with type inference don't do that. I can't look at your code on Github and know what the code is doing. There is not enough information there. Contrast that with nominal type systems where everything I need to know about a function is right there. I don't need an IDE or external tooling to guide me. It's a true mystery how people think they are reviewing pull requests written in TypeScript and actually doing their job correctly. If you so much as have a single @ts-ignore or "any" in your code base, then all bets are off on correct behavior.

TypeScript diminishes the point of type systems by giving the developer too many escape hatches. You ever see a developer abuse the non-null assertion operator in a case where it's not at all true that the value is not null, but the developer doesn't care because they just want to make the compiler happy? Yeah. I have. Then you have to explain the purpose of the non-null assertion operator and that goes about as well as you can imagine.

If a type system isn't helping you understand your code or organize your code, then it's all rather pointless. TypeScript is incredibly complex. How many people using TypeScript can tell you something as basic as the difference between "interface" and "type"? And try to describe the difference without using sentences such as "well, originally..." and "back in 2017...".

edit: Oh, and in case anyone thinks I'm being pedantic on that last point, check out this absolute shit show:

https://stackoverflow.com/questions/37233735/interfaces-vs-t...

Keep scrolling down for ultimate depression.

yeah I agree it is kinda confusing. types and interfaces used to be more separate but now they're kinda converging where its confusing when to use which.

I don't think typescript can ever be too strict, there's lots of other regular javascript files and libraries typescript will need to interact with. True it gives you plenty of escape hatches, but the point is to have less escape hatches than javascript. Your team can discuss what should or shouldn't be allowed, I'm not sure how having strict typescript is really solving the problem of mis-aligned expectations for what is allowed code.

Regarding the non-null behavior I've found that the real problem usually isn't with the variable's type definition but with it being a 'global' or not properly defined with the constructor. One can't define it as non-null because they don't know when it is constructed aka they add a property to a class that they plan on adding later when they should probably either fetch that property first or ensure it is done in the constructor.

To be slightly fair you do get similar stuff in say java where interfaces slowly gained default methods and got closer to abstract classes.

> If you so much as have a single @ts-ignore or "any" in your code base, then all bets are off on correct behavior.

I've been writing a ton of TS since 2016 and in most of those projects we've had liberal use of 'any' or even incorrect types (!). It's still far better than vanilla JS. The argument that "it doesn't work all the time therefore it's worse" makes no sense to me.

Ultimately the issues you're talking about make logical sense academically, but nobody has ever raised them as problems in the real world that I've ever encountered.

Full typing makes my job easy, but a little bit of typing is still better (in most cases) than no typing at all. There is an argument to be made about simplicity for small codebases, but those aren't the ones I'm talking about.

Color | undefined; tells me that the dev who wrote the code doesn't know whether they will pass the parameter or not, it gets decided at runtime. Not only does this complicate the function signature, it inevitably adds a conditional inside that method -- or passes the buck down the line to another method called from within.

At that point, TS is just a burden.

I had the opposite experience. I like languages with static types a lot and I used to be a fan of TypeScript.

After using it for a few OSS projects and professionally, I came to the conclusion that, if I need types, I'll use a real statically typed language if I need types and JS for what doesn't matter.

TypeScript works but: - it's a major pain to work with if your dependencies don't have type definitions - if another developer in your codebase left a hole somewhere you won't be able to rely on your types, you'll get runtime errors and you basically ruin the capability to reason around your code - it's slow (hopefully deno's efforts will bring some news in that regard)

TL;DR: TypeScript should be much stricter and don't compromise with JS.

Yep. My experience as well. In a large project, the types often end up being pointless since you can easily just ignore them at any time, and the "compiled" JS will just happily go along with it all. Eventually I hit a point where I wonder why we're even bothering using types.
bingo.

TypeScript had absolutely no excuse to keep JavaScript, beyond the misguided idea that because so many JavaScript developers exist that it should be easy to get them to learn types.

First, they introduced a compiler. At that point, you may as well toss out JavaScript for a better language that actually has nice type syntax. TypeScript syntax is horrible. It makes JavaScript completely unreadable in many places.

Second, JavaScript developers tend to be the type that have never learned another language beyond JavaScript. They come from bootcamps and elsewhere that only teaches JavaScript and that's all these developers know. They do not have the experience necessary to not make a mess of TypeScript's structural typing system. You'll end up with incoherent ad hoc types all over the place. You still have JavaScript's ad hoc data flow all over the place, but now you have TypeScript yelling at you while trying to formalize the mess and failing badly at it.

What I find is that often when people say they like TypeScript they really mean they like VSCode.

TypeScript is unnecessary. Type incompatibility is not a big problem at all... It's the easiest kind of bug to find and fix. The simplest tests will typically catch those bugs even when the tests are not explicitly looking for them.

On the other hand, issues related to parallel async state mutations which involve race conditions are very common in complex projects and are very challenging to debug. Type incompatibility issues are a walk in the park. You don't need a type checker for that. Just because your boss is incontinent doesn't mean that you also need to wear an adult diaper!

I have a theory that TypeScript was not actually invented for frontend developers, it was created for non-frontend developers who were forced to deal with the frontend, and hated JavaScript.

That's also why I think it's very funny that JavaScript developers are starting to use TypeScript on be backend, i.e. Deno.

You are using the patch to JavaScript that backend devs needed to make JavaScript more like backend languages in the very place that the languages it is trying to be like already exist. It's ludicrous.

Yes.

I write large Node projects and Electron apps and TS has been a huge help.

Looking at the Internet Archive for typescriptlang.org, most (but not all) of their original samples are doing something in a browser[0], so while it definitely became popular with backend developers, I don't know if I would go so far as to say it was created for non-frontend developers who were forced to deal with the frontend.

[0] https://web.archive.org/web/20121004030106/http://www.typesc...

I don't understand any arguments of this variety because TS just compiles to JS

TS hasn't changed the way I write applications too much, besides giving me a bit of safety/enforcement of good practices and productivity boost

It's the extra layer of convolution. It's necessary in a browser, but when circled back to the server, it's vestigial.

With Deno, you're using a classic OOP inspired superset of JavaScript, transpiling it to JavaScript, and running it in a JavaScript engine on a server. That's only necessary in a browser.

On a server, if you don't want to use JavaScript, you can just directly use a classic OOP language, and dispense with the whole extra frontend transpilation abstraction.

Unless you're a frontend-only webdev, and, understandably enough, you don't want to learn a classic OOP language, which I believe is the real reason.

Or, at least I hope so, because the only other possible reason is mass obliviousness, and that's horrific.

Two downsides to TS: an extra step to transpile, and having to write shims when it bugs out and can't verify an external declaration.

Upsides? When tuned to its most pedantic settings I've uncovered a handful of bugs that would have otherwise launched.

That's worth far more than a few tiny annoyances. tslint > jslint.

If you are working on the server side, ts-node has been a huge timesaver. It removes the extra step of compiling and lets you just run your script with `ts-node script.ts` or even `#!/usr/bin/env ts-node` at the top of your file works.

https://github.com/TypeStrong/ts-node

babel-node is also very nice, and more lenient about type checking. i've discovered nodemon with babel node gives me the ability to hack something together and adding a tsc build pipeline helps me correct the types when i'm done
Thanks for the node tip!

I built an electron webpack framework and it uses hot reloads, but since i fork a lot of processes, it doesn't quite understand those need to be reloaded too! Got a fix for that by any chance?

FYI, tslint is deprecated since 2019.

Most people use eslint. But eslint on TS is excruciatingly slow.