Discussion was locked because I moved away from DEV to blog on my personal website. If you want to chat about the content of any article, hit me up in Mastodon.
You can see pretty much daily here in DEV an article about how great or how bad TypeScript is, and then folks that don't share the opinion debating in the comments.
Generally, the arguments for both "sides" are understandable. The people that support TypeScript will tell you everything about how great the DX is, and how the learning curve is worth it, while the people against it will tell you that the learning curve is too steep, and the added value of TS is too low to consider it.
I'm ok with both opinions because at the end of the day they are just opinions and as such, you should use the tool that is more convenient for you, and TS is not for everyone. My problem is when the arguments are straw mans and today I'll focus on one that I have seen a lot recently:
The argument
"TypeScript isn't useful because it doesn't do runtime type checking"
The problem with this argument is not that it's "against TS", but actually that it's asking something from TS that doesn't even exist in other typed languages like it. The argument is based on ignoring the difference between type checking and data validation.
The examples that folks use when they present this argument are usually APIs, file system access, user input, and other types of "unpredictable data". They say that even if you type those, you could get unpredictable data, so "TS is useless". This is a "straw man" because it presents an external problem unrelated to type checking, and then uses it as an argument against it.
TypeScript is a tool for developers, not for consumers. As such is at the same level as JSDocs, ESLint, prettier, and other dev tools. It allows you to catch some errors earlier than prod in your editor, but once is "compiled" is just JavaScript, so is your responsibility as a dev to validate data you "can't trust".
So, a function like this in TS is just fine:
const add = (value2: number) => (value1: number) => value1 + value2;
Because when you try to use it passing strings for example, it will yell at you in dev time. But now if we do something like this:
fetch("https://swapi.dev/api/people/1")
.then(response => response.json())
.then(({ name }: People) => console.log(`Hello ${name}`))
.catch(console.error);
We are doing things wrong, mainly because we are typing that response as People
and maybe we got something else from the API. In those scenarios you have several options, one is to use something like Partial
which makes all the properties of an object optional, so TS will tell you that name
could be undefined:
fetchPromise.then(({ name }: Partial<People>) =>
typeof name === "string"
? console.log(`Hello ${name}`)
: Promise.reject("Response is not of type People"),
);
Another solution is to have an abstraction layer on top of the API that generates the types and fallback values for you (you need to have a contract with your API, using stuff like swagger, GraphQL, or others). You can also use libs such as io-ts which does all the runtime checking for you while keeping it type-safe in dev.
Now, going back to the argument: Saying that TS is useless because it doesn't do validations at runtime is just saying that you missed the point of TS completely. It would be the same as saying that ESLint is useless because it doesn't throw in prod when the user doesn't follow a linting rule, or it would be like asking Prettier to throw if the code is not formatted correctly in production.
TypeScript is a tool to help in the dev process, and if you tried it, you know that even if it doesn't do validations at runtime, is extremely valuable as a tool for refactoring, documentation, autocompletion, and so on.
Now, it would be a false argument as well to say that the only way of achieving this is with TypeScript. You can also use JSDocs and type your code with it, and thanks to the TS server you can get almost the same experience in vanilla JS.
Closing
Hope this article is useful enough to understand that your argument is not valid because:
- TS is not designed to do validations in production.
- All typed languages have the same issue, you should never trust stuff like user input, the file system, APIs, or any other "external source".
At this point then you might understand why I say that the initial argument is a straw man because if we fix it, that argument is actually saying this:
"TypeScript isn't useful because it doesn't do something it wasn't designed to do in the first place"
Which is kinda ridiculous.
That's it, thanks for reading!
Cheers!
Top comments (24)
Nice counterpoint!
I like vanilla JavaScript for small projects.
But I would not do a large project in JavaScript.
I'd use TypeScript, CoffeeScript2, or Elm.
(I've used TypeScript for a large project. It was fabulous. But my next large project I'll probably use Elm because of my interest in functional programming.)
I was really interested in Elm but it has been stale for almost 2 years (no new releases). And you also have to consider that SSR is not supported by Elm, the current way of achieving it is basically running a headless chrome instance on the server and getting the generated html from that, and then replacing everything with the dynamic version (which kinda sucks because your running a browser on the server killing resources, and you're making your users download the same thing twice).
I'm not familiar with how SSR is normally done.
If the server side rendering does the rendering on the server, how is that normally not generated on the server?
According to Evan Czaplicki, Elm is still under active development, but is at a currently at a stable level. (I haven't played with Elm since 0.3, so that was quite a while back now. And Things Have Changed. I've heard a lot of folks annoyed that Elm has had such big not-backwards-compatible changes. Rather like Haskell in that regard.)
AFAIK the current version of elm is
0.19.1
and it was released on October of 2019 ... so even if is stable, is "stale" (no improvements or new features in years). SSR in nowadays WebDev means doing the first-paint of the app in the server, and then load only the necessary JS for the interactive parts as the user needs. Sadly elm's current implementation does all the rendering and logic on the client-side, so if JS is disabled/blocked or taking a lot to load (like in a really poor connection) it doesn't work at all. You can see an excellent SSR implementation in Remix.C++ had no new releases for 13 years. shrug
JavaScript ES3 was 1999, ES5 was 2009, and ES6 was 2015. shrug
Those long stretches of no new releases did not merit them being stale and walking away from them.
Two years, two months isn't that long.
????
2 years without any updates is a lot in tech.
C++98 (1997-Nov) to C++11 (2011-Mar).
Yes, everything has older versions, that doesn't mean that the development of those languages is stale if they keep releasing new versions ... C++ released newer versions more recently than Elm :/
My point was that C++ and JavaScript have had long stretches where they did not release a new version. The recent releases of C++ were 3 years apart. 3 years is greater than 2 years.
Elm is great, but fp-ts seems a safer and more practical alternative for FP
Thanks for mentioning JSDoc. I like typed languages, because debugging is way easier. Since JSDoc is optional, you still have freedom. When writing enterprise software for bleeding edge hardware, it is not necessary to transpile and polyfill like TS does.
I love the fact that we can "use TS without TS" trough JSDocs, so I had to mention that. When I want to write something quick and dirty in JS, I can just use JSDocs and still get a similar DX to what I would get with TS. Still my point is that types (either if you use them trough TS or JSDocs) are useful for DX, and asking for those to do the validations for you is kinda ridiculous π€£
As somebody currently trying to learn what TypeScript is and isn't, and when it's useful, thank you for this! I've been trying to learn by converting a small working vanilla JavaScript app to TypeScript to see what changes are needed and using strict validation, and there seem to be quite a few points around consuming APIs that seem... questionable?
As a backend developer (mostly C#) the strong typing appeals to me and makes it easier for me to adopt and be more sure of the application code I'm writing. Great to see your insights here, thank you!
If you were doing C# the migration to TS should be pretty straightforward, mainly because it was created by the same guy. I mentioned it in the post, but for consuming APIs, io-ts might help a little. It validates types at runtime, and generates the types for TS, so you don't end up "writing types twice".
I'm glad this was useful for you! π
I want to see anyone maintaining high-scale project with clear javascript
It is possible and way more common than you think. I prefer to work with TS, but there are huge projects out there that don't use TS at all, and have lots of conventions, tests and linting rules to avoid issues.
Yes. And then comes by a new developer - understands probably nothing.
And yes, you could use JSDoc. But WHY? Why so anti-TS.
Using Typescript can ease your project with most popular programming design patterns as well, and best practices.
Using JSDocs is not "anti-TS", is still using TS just in another way. I agree that TS is great for big projects (I use it always), but the fact is there are still lots of projects out there not using TS and that's fine. I also saw more than once folks with TS projects that were even worst than vanilla JS projects. TS doesn't inherently make your code better, and vanilla JS doesn't inherently make your code worst, is actually the way you use it that matters. A few examples to illustrate:
As you can see, languages has nothing to do with the quality of the product, but actually what we do with those languages is what matters.
Still, the point of the post is that folks sometimes are against TS for the wrong reasons, but that also applies to some folks that are "pro-TS" for the wrong reasons as well.
Cheers!
Hi Luk, nice article!
I am wondering why would people point lack of runtime data validation in typescript as a downside? As you wrote, in strongly typed languages, types are checked during compilation. I mean that's the point of strongly typed languages, isn't it?
You mentioned that you are interested in functional approach, so you might be interested in the SAFE stack, which lets you write frontend in F# with the "elmish" flavor.
I don't know why folks do that, but they do. I wrote this article after I discussed for the third time that TyprScript is not designed for that, and they were like: "Then TS is useless" π€¦π» ... About F#, I tried it with Fable, but it isn't my cup of tea. If I have to do pure FP for web, I'll use Elm (even if Elm is kinda stale).
As far as I know that's not really true, since ReasonML seems offers that and I assume there are other languages on the "more functional side of life" that also have type information and do data validation at runtime.
I don't know if in elm it's only JSON input that get's validated or there is more, and the same goes for kotlin/js...
Just wnated mention those options, even though they come with their own learning curve and I'm not able to provie more details without doing some research. Maybe people knowing those languages can offer more insights.
ReScript (the successor of ReasonML) doesn't do runtime validation, which is a common misconception. Take a look at this. We have 2 inputs with two
Int
states, and we show the total. We start the states in1
to show that the result is correct (2
), but as soon as the user uses the inputs (that actually return strings) we end up doing a concatenation of those strings and ReScript doesn't complain, even when those were supposed to beInt
s (here's a SandBox showing it). ReasonML/ReScript have more strict types in dev and are more functional than TS, but still they don't do validations, they do type checking in dev as TS does.This is only user input, but the same happens with API responses, or anything "out of our control". Typed languages can do assumptions until a point, but once we reach that surface of contact with the "external world" we need to start doing validations, type conversions, formatting and so on. That's why in TypeScript we have tools such as io-ts.
Well put Luke, that is something that has been niggling at me too and this is a great analysis of that argument. I'll avoid weighing in on the wider debate :)
I really like TS, but this post is not "me defending TS" (the wider debate) but more like "me helping the folks that are against TS for the wrong reasons". I could easily write a a similar article called "Those TypeScript defenders..." for folks that defend TS saying "it replaces tests", or "you can omit validations", or stuff like that, which are also wrong π