TypeScript Gotchas
Tags: typescript
Published: 2020-03-14 11:09:35

---
title: TypeScript Gotchas
published: true
description: How to avoid runtime errors in your TypeScript code
tags: typescript
---

TypeScript can be a great language to work with. If you're starting a new mid to large project in JavaScript, I would certainly recommend using TypeScript at the moment. Knowing your code is correctly typed brings a lot of confidence when it comes to making changes.

However in order to get this great experience, I always have a few "gotchas" in the back of my head while writing TypeScript. These are things to watch out for, which can cause runtime issues if you're not careful.

> This can provide us with a "sound" codebase, even though TypeScript is inherintly an "unsound" type system.

_These examples have been tested with TypeScript v3.8.3_

### 1. Beware `JSON.parse` and `.json()`

```ts
const obj = JSON.parse('{ "x": 5 }');

obj.hello(); // uh-oh! obj.hello is not a function
```

```ts
const data = await fetch("https://pokeapi.co/api/v2/pokemon/1/").then(res => res.json());

data.hello(); // same again!
```

Here `obj` and `data` are returning the type `any`, even in strict mode, and so TypeScript doesn't warn you about these runtime errors.

Solution: you should decode anything that you don't know the type of. I recommend bringing in a library like [io-ts](https://github.com/gcanti/io-ts) for the job. It's also really useful for query parameters.

### 2. Beware record types

```ts
type MyObj = Record<string, string>;

const myObj: MyObj = {};

const result = myObj.hello;
```

What's the type of `result`? TypeScript tells me it's `string`, but actually it's `undefined`!

I still find `Record` to be useful in some cases but you really need to be careful with it. You could also consider using something like [fp-ts lookup](https://gcanti.github.io/fp-ts/modules/Record.ts.html#lookup) in order to be extra careful.

### 3. Beware type assertions

```ts
type MyObj = { hello: () => void };

const myObj = {} as MyObj;

myObj.hello(); // uh-oh! myObj.hello is not a function
```

Just avoid `as` and angle bracket type assertions as much as you can.

### 4. Beware `catch`

```ts
try {
    throw undefined;
}
catch (e) {
    e.hello(); // uh-oh! Cannot read property 'hello' of undefined
}
```

Here `e` has type `any` so no type errors are reported. Interestingly if you try to annotate `e` like

```ts
catch (e: Error) {
```

TypeScript won't let you do that which is good. But without it it's still returned as `any`. 🤷

Watch out for `.catch` on a `Promise` too.

### 5. Don't use `any` or `!`

By now this should go without saying. Using `any` breaks down the point of bringing in TypeScript and you loose all of your confidence everything is sound and runtime error free.

`!` can be very convenient but it's best avoided. My only exception is in test code.

Avoid things like this

```ts
function lowercaseOptional(val?: string) {
    return val!.toLocaleLowerCase();
}
```

and prefer explicitly handling the `undefined` case like this

```ts
function lowercaseOptional(val?: string) {
    if (!val) return "";
    return val.toLocaleLowerCase();
}
```

You can ensure `any` and `!` aren't used by setting up the eslint rules [no-explicit-any](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md) and [no-non-null-assertion](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md).

### 6. Be careful with libraries!

There are some libraries which served their purpose brilliantly in JavaScript. But in TypeScript, it's simply not feasible to keep using the same API while also keeping your code sound.

This example is taken straight from the [lodash doc](https://lodash.com/docs/4.17.15#flow) and converted to TypeScript:

```ts
function square(n: number) {
  return n * n;
}

var addSquare = _.flow([_.add, square]);
addSquare(1, 2);
```

What's the return type of `addSquare`? `any` ! 😩

### Honourable mention - arrays

```ts
const array: string[] = [];
const val = array[1];
```

TypeScript tells us `val` is of type `string`, when really it's `undefined`. However this is a gotcha in most programming languages, and it really wouldn't be practical to check for optionals in something like a `for` loop. But it's good to be aware of.

Do you have any more "gotchas" I'm missing? Please share in the comments below!