That TypeScript argument...

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:

  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!

Leave a comment