5 TypeScript tips to improve your architecture
Tags: typescript
Published: 2019-08-26 12:40:33

---
title: 5 TypeScript tips to improve your architecture
published: true
tags: TypeScript
canonical_url: https://nehalist.io/5-typescript-tips-to-improve-your-architecture
---

 ![5 TypeScript tips to improve your architecture](https://ghost.nehalist.io/content/images/2019/08/5-ts-tips.png)

Even though I work pretty much everyday with TypeScript there's something new I learn about frequently. I really enjoy working with it and try to get the most out of it whenever possible - and there's one thing for sure: TypeScript can do _a lot_ of things.

So here are some things I've learned through time which helped me to improve my applications and - ultimately - become a better TypeScript developer.

## Utility types

TypeScript provides some useful types right out of the box - they're called _utility types_ and can be found [here](https://www.typescriptlang.org/docs/handbook/utility-types.html).

One of my personal favorites is the `Readonly<T>` type makes all properties of `T` readonly, making it easy to help you to maintain [immutability](https://en.wikipedia.org/wiki/Immutable_object).

Another handy type is `Required<T>` which makes all properties required.

There's even more with things like `Pick<T, K>` which creates a type of _picked_ properties from another type, `NonNullable<T>` which removes `null` and `undefined` from properties, `Exclude<T, U>` which removes properties from a type and even more - go [check them out](https://www.typescriptlang.org/docs/handbook/utility-types.html), they're really useful.

## Use unknown instead of any

A lot of times if you don't have exact type information you might just go for `any`. And that was actually totally fine - until TypeScript 3.0 introduced the new `unknown` type, the _"type-safe counterpart of `any`"_ (as [described by TypeScript](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type)).

Just like the `any` type you can assign whatever you want to the `unknown` type - but you can't do _any_thing with it like the `any` type allows for:

```typescript
const imUnknown: unknown = "hello there";
const imAny: any = "hello there";

imUnknown.toUpperCase(); // error: object is of type "unknown"
imAny.toUpperCase(); // totally fine
```

Hence `unknown` prevents calling any method on it due to its _unknown_ nature. Additionally assigning `unknown` types to other types is not possible either:

```typescript
let a: unknown = "hello world";
let b: string;

b = a; // error: a is unknown hence it's not possible to assign it to `string`
```

Often times the variable you just declared `any` is actually `unknown` - which prevents faulty assignments or calling methods on it without knowing what methods are provided by it.

See [here](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type) for further information regarding the `unknown` type.

## Lookup types

Lookup types are a neat way to help you finding what you're looking for. Let's assume you've got an object with a dynamic number of properties and want to get a specific property out of it - it's highly likely to do it the following way:

```typescript
const persons = {
  john: { surname: 'Doe' },
  bob: { surname: 'The builder' }
};

persons.john; // equals { surname: 'Doe' }
```

But there's one problem: what if the `john` property does not exist? Your application is likely to tragically die - but that could have been prevented by TypeScript!

By implementing a very small helper we're able to prevent this:

```typescript
function getProperty<T, K extends keyof T>(obj: T, key: K) {
    return obj[key]; // Inferred type is T[K]
}
```

By using this helper we can assure that the property exists when using it:

```typescript
function getProperty<T, K extends keyof T>(obj: T, key: K) {
    return obj[key]; // Inferred type is T[K]
}

const persons = {
    john: { surname: 'Doe' },
    bob: { surname: 'The builder' }
};

getProperty(persons, 'bob');
getProperty(persons, 'john');
getProperty(persons, 'someoneelse'); // Error: Argument of type "someonelse" is not assignable to parameter of type "bob" | "john"
```

It may be common to take use of that within classes which makes the `obj` param obsolete. It might be implemented as a getter for getting a specific item where we want to know which elements are available. In this case we're going to use a fairly uncommon syntax which:

```typescript
class PersonList {
  persons = {
    john: { surname: 'Doe' },
    bob: { surname: 'The Builder' }
  }
  
  getPerson(name: keyof PersonList['persons']) {
    return this.persons[name];
  }
}
```
`keyof PersonList['persons']` is something you probably don't see everyday.

Which would cause calling `personList.getPerson('john')` to be perfectly fine, while `personList.getPerson('jane')` would fail since there's no `jane` key available.

## Extending third-party libraries

If you've ever wanted to extend a third party library but were limited by the public API of that library [module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) is what you're looking for. But when do you want that?

Let's say we've got an Angular application and we want to name our routes. The Angular `Route` interface doesn't provide a `name` property by default - that's were module augmentation comes in handy.

By creating a `typings.d.ts` in the root of our Angular project we can simply tell TypeScript that we want to have an additional, optional property which stores the name:

```typescript
import {Route} from '@angular/router';

declare module '@angular/router' {
  interface Route {
    name?: string;
  }
}
```
It's important to import from `@angular/router` so that TypeScript registers the remaining properties.

Now we can simply add a name to our routes:

```typescript
const routes: Routes = [
  {
    path: '',
    name: 'theMightyDashboard',
    component: DashboardComponent
  },
];
```

Which - for example - can be used within our components by injecting `ActivatedRoute`:

```typescript
constructor(private route: ActivatedRoute) {
  console.log(route.routeConfig.name); // logs `theMightyDashboard`!
}
```
## ES types

Often times I see TypeScript code where most things are properly typed, except native ES elements. Think of something like [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)s, [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)s, [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)s and [all that kind of stuff](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects). But TypeScript provide types for these objects right out of the box;

Maps for example do provide generics to define the contents of our map. So instead of

```typescript
const map = new Map();
map.set('name', 'John Doe');
```

We can improve this by specifying the types of our map key _and_ value:

```typescript
const map: Map<string, string> = new Map();
map.set('name', 'John Doe');
```

This prevents us from accidentally adding a type which is not allowed:

```typescript
const map: Map<string, string> = new Map();
map.set('name', 123); // error: 123 is not a string
```

The same applies to `Set`:

```typescript
const set: Set<number> = new Set();
set.add(123); // Ok!
set.add('whatever'); // Type error
```

Or native arrays:

```typescript
const elements: Array<{ name: string }> = [];
elements.push({ name: 'John Doe' }); // Ok!
elements.push('John Doe'); // Type error
```

If you want to take a look at all types available see the official typings for [ES5](https://github.com/microsoft/TypeScript/blob/master/lib/lib.es5.d.ts) and [ES6](https://github.com/microsoft/TypeScript/blob/master/lib/lib.es6.d.ts) (or all the [other specs](https://github.com/microsoft/TypeScript/tree/master/lib))

## Anything else?

As said in the beginning TypeScript can do so many things that it can get difficult to stay up to date at sometimes. If there's something you really like about TypeScript which you don't see everyday please let me know in the comments!

---

If you liked this post feel free to leave a ❤, follow me on [Twitter](https://twitter.com/nehalist) and subscribe to [my newsletter](https://nehalist.io/#subscribe). This post was originally published at [nehalist.io](https://nehalist.io/5-typescript-tips-to-improve-your-architecture) on August 06, 2019.