Prefer strict types in Typescript
Tags: typescript, types, narrowing
Published: 2024-05-06 13:49:24

---
title: Prefer strict types in Typescript
published: true
description: Strict types reflect reality and reduce a number of potential bugs in runtime
tags: typescript, types, narrowing
cover_image: https://absolyd.com/_astro/post-5-1.BxLmC4H4.gif
# Use a ratio of 100:42 for best results.
# published_at: 2024-05-05 12:20 +0000
---

Types should be as close to reality as possible. Otherwise, they mislead (reduce truthiness in the system) and, thus, slow down development.

Usually, it's self-evident in many statically typed languages, but it's a different case with Typescript since it allows more types flexibility.

## any

[`any`](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any) may be a favorite type for many developers who migrated from Javascript recently. It declares that a type could be anything. There are some use cases for using `any`, but usually it's recommended to not employ it.  One such example I could think of is silencing the Typescript compiler to run a script quickly. Otherwise, it detracts truth from a system and eliminates the benefits of using Typescript.

![any type in typescript visualized](https://absolyd.com/_astro/post-5-1.BxLmC4H4.gif)

If you don't know what your data looks like, how are you going to handle it properly? Without proper handling, a system (or part of it) fails.

The other disadvantage of using `any` is silencing any potential errors you make when handling such data, which leads to another system failure.

```typescript
const anything: any = 'word';
const result = anything * 5;
```

If you remove `any` declaration from the `anything` constant, Typescript tells you what is wrong exactly.

What if I don't know the exact type of data? Imagine I retrieve it from a third-party API that may change tomorrow. Or, from a non-typed `npm` package because it's written in Javascript. This is where we use `unknown`.

## unknown

The useful `unknown` type states that a variable shape is not known and can be anything, the same point as `any` declares. The only difference is that `unknown` requires the compiler to check a type first **before** operating on data.

```ts
const anything: unknown = 5;
const result = typeof anything === 'number' ?
    anything * 5 :
    undefined;
```

The primary use case for `unknown` is to mark variables that are unknown and can be anything, so you must validate a type first before manipulating data.

![unknown type visualized](https://absolyd.com/_astro/post-5-1.BxLmC4H4.gif)

If data shape is known, it's always better to specify an accurate type.

## Narrowing

[Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) is a process of giving types more accurate shapes. I.e., bringing them to reality, so they reflect a real data form.

To illustrate the motivation behind it, take a look at this code:
```ts
const record: Record<string, number> = {
    field: 2,
};
const result = record.field2 * record.field3; // NaN
```

Yes, `record`, is `Record<string, number>`, but it's not accurate enough. `Record` means an object with who knows how many fields and their values are of type `number`. In this case, a more accurate type is `{ field: number; }`. In this example, you can skip declaring a type because the compiler can infer it automatically.

Another example of bad narrowing:
```ts
const fn: Function = (arg: number) => {
    return arg * arg;
};

fn('string'); // NaN
```

Yes, `fn` is a function, but this type doesn't specify the details (and as in the case above, you should skip declaring a type explicitly because the compiler will infer it for you automatically).

## Common types to narrow

For these examples, `T` can be any type.

1. `Record<string, T>`: prefer an accurate object shape, **if you know it**. E.g.,
```ts
type MyObject = { name: string; }
```
2. `T | null | undefined` means you need to handle both `null` and `undefined` cases besides the presence of value. Prefer `T | null`, `T | undefined`, or merely `T`.
3. `Function`: prefer an exact function signature, e.g.:
```ts
type MyFunction = (arg1: number, arg2: string): string;
```
4. `Partial<T>`: prefer a more accurate type.

---
Originally published on [absolyd.com](https://absolyd.com/ts-strict-types)