The developer experience is excellent with Typescript. It's so much nicer to get auto complete suggestions based on a type system rather than mostly heuristics in plain JS. Refactoring is breeze. I enjoy using TS. I enjoy basic correctness guarantees caught at transpile time.
Of course there's trade-offs, but the whole article is a list of unconvincing appeals to authority.
And then the author ends with:
> Our tendency to just blinding reach for tools because it’s what Google or Facebook does often just makes things worse for both developers and the people who use what they build.
You spent 4 paragraphs giving examples of other people using plain JS as the reason we should be blindly switching back to plain JS. Svelte swapped back to JS for n reasons that aren't applicable to you or most people.
> I help people learn vanilla JavaScript, and I believe there’s a simpler, more resilient way to make things for the web.
> I create courses and ebooks, run online workshops, and host the Vanilla JS Podcast. My developer tips newsletter is read by over 14k developers each weekday.
I have not been able to jump on the TS train. For the longest time, it has just not quite felt right. Adding layers upon layers of complexity for a static type system and some related syntactic sugar, all while guaranteeing that it remains a superset of JavaScript has left it in a similar position to C++. It’s fragility has left it with a house of cards-type feel with unending dependency trees and poor visibility into itself as a runtime.
It’s definitely possible to create very brittle types that feel like a house of cards.
I think the most common mistake people make is re-using types. Even if a type works in two places, you can’t guarantee that the semantics in those places will stay consistent.
Devs working in TypeScript often think that in order to keep things DRY you should reuse the same types all over a codebase. For me it’s the opposite: using a type in two places is a code smell. You’re not really leaning on the compiler if you’re re-using types. The compiler loves to take two types and tell you if they’re compatible. Sharing the same type across use cases is just not necessary.
I find keeping types as local to a function as possible helps a LOT with alleviating that “house of cards” feeling you’re describing.
I would hate to make judgement after such a small piece of text, but your argument just doesn't make sense to me. Reusing types would suggest you're actually coding against a design. Why should the data have a different shape in two different places in your code? If you're working with the same data shouldn't its type have a single identity?
> If you're working with the same data shouldn't its type have a single identity?
That's a good instinct, which is probably why people do it in TypeScript.
But, IMO this is not how TypeScript works best. When you write a function, you should fully type the data that you need for that function. This type is semantically tied to the implementation of the function: by writing out that type, you are "certifying" that that is the type that the function needs.
If you then go and use that type in another function, you've lost that "certification" in both places. You now have a type which is the union of the data structure needed by both functions.
This has two negative results:
1) There is no longer any point in the code where you are actually describing the specific contract requested by either function. And,
2) If you want to change Function A, you now have to spend 30 minutes reasoning out whether the contract required by Function B is still compatible with their shared type.
Result 2) is what causes the "I write the code and then I spend 2 hours making the types happy" issue that I was responding to.
Some side notes:
- I can't claim that this is the right approach for all typed languages. But the TypeScript compiler specifically is very happy to exchange one type for another as long as they're compatible. It is designed to handle "duck typing" like this at its core.
- I want to emphasize that syntactically it makes no difference whether you reuse types. And certainly it saves you keystrokes. The problem with reusing a type is you're omitting crucial semantic information about which "slice" of that type is needed for a function.
- I will accept that sometimes it's worthwhile to Pick<> some properties off of an object. But even this is usually avoidable.
I think if you are so reliant on static type checking that you can’t live without it, well, there you go. But it doesn’t seem like it has to be more of a problem than an hundred other oopses you deal with like passing parameters in the wrong order.
Most of JS’s most public issues come from thinking every programming language is
C++.
Statically typing your parameters prevents a lot of instances of passing them in in the wrong order. And if you make a habit of using a single statically typed options parameter instead of multiple parameters that have the same type, you can make passing parameters in the wrong order completely impossible in your codebase.
It’s not a crutch at all, it’s a very useful tool and central to a certain style of programming. There are other styles of programming, like prototyping and scripting, where just relying on yourself not making the mistake in the first place is better.
For smaller applications, an acceptable middle ground is using the TS language server[0] and JSDoc[1] comments.
Writing
// @ts-check
/** @type {number} */
let x;
works very well for example in VSCode. The JSDoc comments are not as convenient as Typescript, but otherwise the experience is almost the same. The language server correctly keeps track of the types for me.
> Incremental compile time when changing files in cli/js takes minutes. This is crushingly slow and painful to modify.
This is in my opinion the worst offender of TypeScript. TSC is insanely slow even in incremental mode for large code bases, and no alternative compiler exists. There are projects which can strip types and convert TS into JavaScript, but they do not check types.
Alternative implementations have been swatted down by TypeScript maintainers like Daniel Rosenwasser, with the leading argument being that the TS spec moves insanely fast and keeping two or more implementations up to spec would not be worth it due to the work it would require.
Well, if there would be multiple TypeScript implementations available, especially ones that are faster, Microsoft would suddenly lose their moat they gathered by getting TypeScript to be wildly used.
Keeping the spec moving fast and breaking it with new updates servers multiple purposes, not only to make the language itself different, but to avoid competing implementations.
When I say "spec" I main the Microsoft TypeScript implementation, as they don't actually maintain any actual specification anymore.
We already have that! And it’s TypeScript. You can and probably do use 98% (made up number but close enough to reality I don’t need to quantify it) of the benefits of TS while authoring JS code if you bother annotating types in JSDoc.
You can also get the same type checking benefits outside of the editor (eg in CI) with very minimal tooling effort. That’s what Svelte is doing.
Granted I work on JS projects which embraced this from the opposite direction (gradual typing where nothing is enforced statically), and it’s strictly worse than just using the tools how they work best (you’d be hard pressed to make an incremental decision about anything without knowingly preserving bugs or increasing the incremental scope).
Anyway, the alternate timeline you seek already exists. It’s just so optional that you could easily miss it.
Typescript is one of the most popular languages in the world at this point and continuing to grow (see Stack Overflow developer survey for the last 3 years). It covers the majority of the mature ecosystem on npm by now. Characterizing it as a trend is beyond incorrect. For all intents and purposes, JS is now statically typed.
TypeScript would have to become more popular than JavaScript for your last part to become true. And it's nowhere near of getting there (outside of SV/hip development world).
I don't think that's a fair metric. Many people using TS (myself included) will typically use "JavaScript" in search queries because the answer is usually the same. The only time I search for "typescript" is if I have a problem with the type checking, which isn't very often.
That said, I think you're right that JS is still quite popular.
I could definitely see the benefit of TS on a giant, modern app with a big team. However, at my job I am a solo developer on literally dozens of smaller JS projects of varying age that I need to keep running essentially forever. Typescript is enough of a pain in my npm build process that I just ditched it long ago. The fewer dev dependencies the better for me personally.
I also work on small apps, and like it there. I needed a day to set up TS the way I wanted, and I don't use strict mode. This way I can have TS check the few places I feel it's most useful, and not bother with the rest.
I feel confident making small components, and functions. But I like adding types to form data and product objects, especially when decorating product objects. Otherwise I'm prone to get confused as to whether the props have been decorated already, or not, maybe check for it? TS just feels really smooth sometimes, but admittedly I am not using it in strict mode.
For small things, sure. Substantial things like apps, its like shooting yourself in the foot before you've ever begun, and good luck refactoring anything with any confidence short of covering your whole app in tests.
Also, I think the examples given in the article are circumstantially exceptional, if you read more closely into the why.
> refactoring anything with any confidence short of covering your whole app in tests
I don't have any confidence in refactor large swaths of code without widespread tests, with or without TypeScript. Types covers such a small area of where we (I) usually introduce bugs.
I've found in strongly typed systems if I lean heavily on the type system refactoring is a dream. I make the changes I want. Then follow the compiler errors. Most of the time it just works. Rarely does that introduce bugs. However if you use a Typed language without using the type system to enforce the design constraints then the type system just gets in your way. In that case refactoring is just as hard. It's very easy in Type Script to write code that might as well be written in a dynamic language. It often is.
That’s not my experience. I find in carefully typed projects I can do pretty large refactors with high confidence that once the types are correct again, a lot of tests will pass, and a lot of behavior will work.
It’s not that there aren’t bugs, but I have a high degree of confidence after fixing a small number of bugs that the overall system has a high degree of correctness.
There are several things that go into this
- good types
- good tests
- good interfaces between modules
All three I think give me a big jump in how quickly and confidently I can refactor.
Tests are very important, don't get me wrong. What I mean is that it's trivial to refactor large _trivial_ things with types -- ie, big restructures, renames, clarifying things, etc -- where without it even small "large" things become dangerous and worksome.
Of course there's trade-offs, but the whole article is a list of unconvincing appeals to authority.
And then the author ends with:
> Our tendency to just blinding reach for tools because it’s what Google or Facebook does often just makes things worse for both developers and the people who use what they build.
You spent 4 paragraphs giving examples of other people using plain JS as the reason we should be blindly switching back to plain JS. Svelte swapped back to JS for n reasons that aren't applicable to you or most people.
> I help people learn vanilla JavaScript, and I believe there’s a simpler, more resilient way to make things for the web.
> I create courses and ebooks, run online workshops, and host the Vanilla JS Podcast. My developer tips newsletter is read by over 14k developers each weekday.
Oh.