How did Typescript disappoint me and is it worth it?

Before starting, I want to mention that I am a TypeScript fan. This is my main programming language for front-end projects on React and for any backend work that I do in Node. I am completely for Typescript, but there are moments that bother me and about which I wanted to tell this article.

I wrote exclusively on TypeScript over the past three years for many different companies, so in my opinion, TypeScript at least does something right or closes certain needs.

Despite its imperfection, TypeScript entered the mainstream development frontend and ranks seventh in the list of the most popular programming languages ​​according to version HackerRank developer skills report.

Any development team, no matter whether it is large or small, writes in TypeScript or not, is always worth it for security:

  • Ensure that well-written unit tests cover as much code as possible in production.
  • Use pair programming: an extra pair of eyes will help catch more serious things than just syntax errors
  • Qualitatively build a code review process and identify errors that the machine cannot find
  • Use linter – such as eslint

Although TypeScript adds an additional level of security on top of all this, but in my opinion, it is very far behind other languages ​​in this regard. I will explain why.

TypeScript is not a reliable type system

I think this may be the main problem with TypeScript, but first let me define what is reliable and unreliable type systems.

Robust Type System

Reliable the type system ensures that your program does not end up in invalid states. For example, if the static type of an expression is string, when it is calculated at runtime, you are guaranteed to receive only string.

In a reliable type system, you will never be in a situation where the expression does not match the expected type, either at compile time or at run time.

Of course, there are various degrees of reliability, as well as various interpretations of reliability. TypeScript is somewhat reliable and catches type errors:

// Type 'string' is not assignable to type 'number'
const increment = (i: number): number => { return i + "1"; }

// Argument of type '"98765432"' is not assignable to parameter of type 'number'.
const countdown: number = increment("98765432");

Insecure Type System

Typescript absolutely openly reports that 100% reliability is not its purpose. Even the non-target number 3 on the list “TypeScript non-goals” clearly states:

Having a reliable or “provably correct” type system is not our goal. Instead, we strive to strike a balance between accuracy and performance.

This means that there is no guarantee that the variable is of a specific type at run time. I can illustrate this with the following somewhat contrived example:

interface A {
    x: number;
}

let a: A = {x: 3}
let b: {x: number | string} = a; 
b.x = "unsound";
let x: number = a.x; // unsound

a.x.toFixed(0); // WTF is it?

The code above does not work because it is known that a.x Is the number from the interface A. Unfortunately, after a couple of feints with reassignment, it turns into a string and this code compiles, but with errors at runtime.

Unfortunately, this expression compiles without errors:

a.x.toFixed(0);

That reliability is not the goal of the language is probably one of TypeScript’s biggest problems. I keep getting a lot of runtime error at runtime that doesn’t catch tsc a compiler, but which would have been noticed by the compiler if a reliable type system existed in TypeScript. TypeScript is now one foot in the camp of “reliable” languages, and the other in the “unreliable.” This half measure approach is based on type anywhich I will talk about later.

I am frustrated by the fact that the number of tests that I write has not decreased at all with the transition to TypeScript. When I just started, I mistakenly decided that I could reduce the burdensome routine of writing a large number of unit tests.

TypeScript challenges the current state of things by arguing that lowering cognitive costs when using types is more important than reliability.

I understand why TypesScript has chosen such a path and there is an opinion that TypeScript would not be so popular if the reliability of the type system were 100% guaranteed. This opinion has not stood the test – Dart language rapidly gaining popularity, along with the widespread use of Flutter. A approvedthat type reliability is the goal of Dart.

The insecurity and the various ways that TypeScript provides an “emergency exit” from strong typing make it less efficient and, unfortunately, make it simple “better than nothing” Currently. I would be glad if as TypeScript grew in popularity, more compiler options became available, allowing experienced users to strive for 100% reliability.

TypeScript does not guarantee any type checking at runtime

Type checking at runtime is not the purpose of TypeScript, so my wish will probably never come true. Run-time type checking is useful, for example, when working with JSON data returned from API calls. It would be possible to get rid of a whole category of errors and a lot of unit tests, if we could control these processes at the level of the type system.

Since we cannot guarantee anything at runtime, this can easily happen:

const getFullName = async (): string => {
  const person: AxiosResponse = await api();
  
  //response.name.fullName may result in undefined at runtime
  return response.name.fullName
}

There are several supporting libraries such as io-tswhich is great, but that could mean you have to duplicate your models.

Scary type any and strict option

The type any means “any”, and the compiler allows any operation or assignment of a variable of this type.

TypeScript works well for small things, but people tend to put the type any on anything that takes more than one minute. I recently worked on an Angular project and saw a lot of code like this:

export class Person {
 public _id: any;
 public name: any;
 public icon: any;

TypeScript lets you forget about the type system.

You can break the type of anything with any:

("oh my goodness" as any).ToFixed(1); // remember what I said about soundness?

Option strict includes the following compiler options, which make everything more reliable:

  • –strictNullChecks
  • –noImplicitAny
  • –noImplicitThis
  • –alwaysStrict

There is also an eslint rule @ typescript-eslint / no-explicit-any.

Spread any may ruin the reliability of your code.

Conclusion

I have to repeat that I am a TypeScript fan and use it in my daily work, but I feel that it is imperfect and the hype around it is not entirely justified. Airbnb claims TypeScript helped prevent 38% of errors. I am very skeptical about the percentage so accurately stated. TypeScript does not improve or combine all existing practices of good code. I still have to write a ton of tests. You could argue that I write more code, so I have to write so many tests. I keep getting a lot of unexpected runtime error.

TypeScript offers only basic type checking, and the fact that reliability and type checking at runtime is not its purpose leaves TypeScript in the world of half measures, halfway between the best world and the one in which we code now.

TypeScript is great thanks to the good support of IDEs such as vscode, where we get visual feedback during the printing process.


TypeScript error in vscode

TypeScript also improves refactoring and code-breaking changes (such as changes in method signatures) that are instantly identified when the TypeScript compiler starts.
TypeScript provides good type checking and it is definitely better than no type checking or simple eslint, but I feel that TypeScript can be much better and the necessary compiler options could please those who want more from the language.


Subscribe to our Instagram developer

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *