DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for That TypeScript argument...
Luke Shiru
Luke Shiru

Posted on • Updated on • Originally published at lukeshiru.dev

That TypeScript argument...

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;
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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"),
);
Enter fullscreen mode Exit fullscreen mode

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:

  1. TS is not designed to do validations in production.
  2. 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)

The discussion has been locked. New comments can't be added.
Collapse
 
eljayadobe profile image
Eljay-Adobe

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

Collapse
 
lukeshiru profile image
Luke Shiru

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

Collapse
 
eljayadobe profile image
Eljay-Adobe

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

 
lukeshiru profile image
Luke Shiru

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.

 
eljayadobe profile image
Eljay-Adobe

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.

 
lukeshiru profile image
Luke Shiru • Edited on

????

  • C++20 (ISO/IEC 14882:2020): 15 December 2020
  • C++23 (Preview release): 23 October 2021
  • ECMAScript 2021: June 2021
  • ECMAScript 2022 (Preview release): 22 July 2021

2 years without any updates is a lot in tech.

 
eljayadobe profile image
Eljay-Adobe

C++98 (1997-Nov) to C++11 (2011-Mar).

 
lukeshiru profile image
Luke Shiru

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 :/

 
eljayadobe profile image
Eljay-Adobe

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.

Collapse
 
tomliang profile image
Tom Liang

Elm is great, but fp-ts seems a safer and more practical alternative for FP

Collapse
 
philipp__6424ee1faaca7b14 profile image
Philipp

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.

Collapse
 
lukeshiru profile image
Luke Shiru • Edited on

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 🀣

Collapse
 
jasonstcyr profile image
Jason St-Cyr

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!

Collapse
 
lukeshiru profile image
Luke Shiru

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! 😊

Collapse
 
talr98 profile image
TalR98

I want to see anyone maintaining high-scale project with clear javascript

Collapse
 
lukeshiru profile image
Luke Shiru

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.

Collapse
 
talr98 profile image
TalR98

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.

 
lukeshiru profile image
Luke Shiru

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:

// Bad JS

const a = x => y => y * x;

// A little better JS

const times = multiplier => multiplicand => multiplicand * multiplier;

// Good JS

/**
 * Curried multiplication function.
 * @param {number} multiplier The number to multiply by.
 */
const times = multiplier =>
    /** @param {number} multiplicand The number to multiply. */
    multiplicand => multiplier * multiplicand;

// Bad TS

const a = (x: any) => (y: any) => x * y;

// A little better JS

const times = (multiplier: number) => (multiplicand: number) =>
    multiplicand * multiplier;

// Good TS

/**
 * Curried multiplication function.
 * @param multiplier The number to multiply by.
 */
const times =
    (multiplier: number) =>
    /** @param multiplicand The number to multiply. */
    (multiplicand: number) =>
        multiplier * multiplicand;
Enter fullscreen mode Exit fullscreen mode

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!

Collapse
 
szymonszym profile image
szymon-szym

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.

Collapse
 
lukeshiru profile image
Luke Shiru

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

Collapse
 
karfau profile image
Christian Bewernitz

but actually that it's asking something from TS that doesn't even exist in other typed languages like it.

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.

Collapse
 
lukeshiru profile image
Luke Shiru • Edited on

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 in 1 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 be Ints (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.

Collapse
 
miketalbot profile image
Mike Talbot ⭐

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

Collapse
 
lukeshiru profile image
Luke Shiru

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 πŸ˜…

New programmer and javascript

Stop by this week's meme thread!