TypeScript > JavaScript: 4 Problems TypeScript Solves And Why You Should Switch Today

TypeScript > JavaScript: 4 Problems TypeScript Solves And Why You Should Switch Today

·

5 min read

"TypeScript is just JavaScript with type safety!"

You've probably heard it described this way before. Now while it's true, it doesn't explain why that's useful or what problems it can help solve.

In this post, we'll take a look at some problems with JavaScript that TypeScript helps address.

A lot of JavaScript code is filled with problems that waste your time. This isn't always true, but most JavaScript codebases I've seen suffer from these problems:

  • Poorly Documented Code
  • Testing Invalid Use Cases
  • Longer Feedback Loops
  • Difficulty Refactoring

Let's take a look at how TypeScript solves these problems.

1. Poorly Documented Code

Code that isn't documented is harder to understand.

You end up having to step through entire function chains to make sense of anything. You flip between files, you lose your place, and you move slower. While documentation can be good in JavaScript projects, that's not always the case. It's often skipped or not updated which leads to outdated information.

Wrong documentation is worse than no documentation.

TypeScript improves this by forcing you to document code next to where it's defined. This makes the code more maintainable because it's easier to jump back into the code later. You spend less time trying to understand the code and more time writing new code.

Ambiguous Functions

function add(a, b) {
  return a + b;
}

A lot of times JavaScript functions can be ambiguous.

In the example above, it looks like the add function takes two numbers and returns the sum. The problem is that the + operator is overloaded.

It can also be used to concatenate two strings:

add("Hello", " World!"); // Hello World!

That's probably (hopefully) not what you wanted!

But if you didn't write the code, how would you know? The only way to know for sure is to check every instance of the add function to see if strings are used.

TypeScript can make this better by explicitly defining types in the function.

/**
 * This implicitly has a return type of number so
 * the last number can technically be left off
 */
function add(a: number, b: number): number {
  return a + b;
}

The types guarantee the add function is used correctly throughout your app. The only way for it to be used incorrectly is for someone to pass an any type value to the function.

add("Hello" as any, " World!" as any);

This kind of stuff should be avoided and would hopefully be caught in a code review.

Modeling Complex Objects

If you're working on any decent-sized project, you'll end up using complex objects.

Let's say you had a list of blog posts on a page. Each blog post could be represented as an object with several properties. Some of these properties might even be nested. Without TypeScript, you'd have to know what those properties are without any context.

It makes it difficult to jump into a new codebase because there's nothing that explains what a blog post is.

With TypeScript, you have a way to describe that model directly in your code.

type BlogPost = {
  title: string;
  slug: string;
  content: string;
  commentIds: string[];
};

2. Testing Invalid Use Cases

Without TypeScript, ambiguous JavaScript code can be tested when it's not needed.

Let's look at the add example from above again.

Without TypeScript, it's not clear if the expected input should only support numbers. Since you don't know, you could decide to test the function with other inputs like strings.

function add(a, b) {
  return a + b;
}

// The example uses common testing syntax
// (check out jest if you want to learn more)
it('should add two numbers', () => {
  expect(add(1, 2)).toBe(3);
});

it('should concatenate two strings', () => {
  expect(add('Hello', ' World!')).toBe('Hello World!');
});

The second test is unnecessary, but there's no way to know that by looking at the code.

In the TypeScript version, it's clear that the second test is invalid. Not only is it invalid, but it'll fail the type check. The TypeScript compiler will throw an error if you try to pass a string to the add function. It saves you time and you have a clearer picture of how the function should behave.

3. Longer Feedback Loops

The secret to a better developer experience is a tighter feedback loop.

The less time you waste waiting for feedback, the more time you can spend on development. TypeScript has a much quicker feedback loop than JavaScript. The TypeScript compiler validates your code before it runs.

The only way to check if JavaScript is valid is to execute it.

TypeScript is even faster if you integrate it with your IDE. As soon as you make a mistake in your code, you'll see an error without having to leave your editor. You're able to move faster because there's less context switching involved.

The speed improvements you gain from TypeScript make the developer experience better.

4. Difficulty Refactoring

Refactoring JavaScript is more difficult than refactoring TypeScript.

Let's say that you wanted to change the parameter type of a function:

// Before
const addFive = (a: string) => Number(a) + 5;

// After
const addFive = (a: number) => a + 5;

The change makes sense and it doesn't seem like it'll have a big impact.

But how would you know?

You could try searching for the function in your code. But then you'd have to waste time understanding how it's being used in every instance. The problem gets even worse if the function is passed as an argument to another function.

function doWork() {
  // ...
  // you have to find x and check its value
  addFive(x);
}

function fnWrapper(fn) {
    // ...
}

function doMoreWork() {
  // you have to understand how fnWrapper works
  fnWrapper(addFive);
}

The change we made to addFive was easy, but refactoring the code that uses it isn't. You end up having to walk entire function chains for the most basic change. It's a slow process and error-prone.

TypeScript can solve these problems elegantly

TypeScript makes it obvious when things are broken through the compiler.

With a good IDE, you can see that directly in your editor. Even without IDE support, you're notified during the build or validation step. This means you find problems faster and fix them before they ever reach the end user.

I'd recommend trying TypeScript in your next project and seeing the benefits for yourself.