---
title: Going through changes with TypeScript
teaser: 'When we inevitably make changes to our codebase, any help is appreciated,
  and a type system can be your best friend.

  '
tags: typescript,types,web
author: Alejandro Dustet
published_on: 2020-03-30
---

![typescript compiler vim](https://images.thoughtbot.com/blog-vellum-image-uploads/2yabHIeQuCRRPuc5tcXQ_vim-tsserver.jpg)

Software is always changing, and it is our responsibility as developers to build
solutions that can adapt to these modifications. Type systems are an
extraordinary tool to build flexible solutions that can evolve to our
ever-shifting needs.
From my time working with TypeScript, I want to share an experience where the
type system was essential to this goal. I bring an example where we produce an
event, and we also need to handle it; it can be a user input, new push
notifications, or an update in the underlying data.

Typically, we might have different places and methods to deal with each event.
There are several [benefits] to this approach; having each class be responsible
for one thing is one of them. However, there is a price we have to pay when we
break responders for the same events; we now have more modifications to perform
once the original solution shifts. We have to deal with many responders and
handlers; we have to keep many locations updated. With a type system, we define
the way everything is going to be managed and emitted, by presenting a complete
API to respond to all the cases. Let's take, for example, a new user status
event handler:

[benefits]: https://thoughtbot.com/blog/back-to-basics-solid

``` typescript
  interface UserStatusResolver {
    userOnline: () => void,
    userOffline: () => void,
  }
```

With this definition, in our implementations, we respond to each message.

``` typescript
class MenuUserStatusHandler implements UserStatusResolver {
  userOnline = () => { ... }
  userOffline = () => { ... }
}
class UserStatusHandler implements UserStatusResolver {
  userOnline = () => {  ... }
  userOffline = () => {  ...  }
}
```

By making use of a resolver interface declaration, we make sure only to emit
messages that can be handled by our resolvers:

``` typescript
type UserStatusEvent = keyof UserStatusResolver
```

The presence of a [type alias] is to use [computed properties]. The downside is
that type aliases can not [extend] another type alias. So if we need a more
granular control for our event emitters, an alternative is to use `Pick`,
`Omit`, and some [type intersection].

[type alias]: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases
[computed properties]: https://pawelgrzybek.com/typescript-interface-vs-type/#type-aliases-can-use-computed-properties
[extend]: https://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases
[type intersection]: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#35-intersection-types

``` typescript
type UserLeftEvent = keyof Pick<UserStatusResolver, "userOffline">

type UserJoinedEvent = keyof Pick<UserStatusResolver, "userOnline">

type UserStatusChangeEvent = UserLeftEvent & UserJoinedEvent
```

## Going through changes

This post is about changing things, so let's shake things up. If we need to
respond to new user status; we can add it to the interface declaration and have
the type system leads us through the changes. If we add a new `userAway` event,
we can follow error messages like:

```
Class 'MenuUserStatusHandler'
incorrectly implements interface 'UserStatusResolver'.

Property 'userAway' is missing in type 'MenuUserStatusHandler'
but required in type 'UserStatusResolver'.
```

We now have the compiler handling the heavy lifting of remembering where to
respond to that event. Thanks, compiler! To take full advantage of this, we want
to have our development environment integrated with these benefits. Here are a
couple of resources depending on which text editor you use:

- [Vim](https://thoughtbot.com/blog/modern-typescript-and-react-development-in-vim)
- [Visual Studio Code](https://code.visualstudio.com/docs/typescript/typescript-compiling)
- [Sublime Text](https://github.com/Microsoft/TypeScript-Sublime-Plugin)

## Conclusions

Handle all the cases, consider each alternative, make sure our implementation is
complete. Sound familiar? If you use code to solve problems, you probably hear
it a few times a day. A fully deterministic system is our eternal pursuit as
software developers, and with dynamic and complex problems that change
continuously, achieving this is particularly difficult.

Our solutions are bound to change, so we should make use of all the available
tools to direct us through them. Having a flexible implementation is a great
asset, and the features of a type system can guide us as requirements evolve.
The ability to combine interfaces is a powerful TypeScript feature that can make
our solutions more and more deterministic. We can provide a full
description of the problem through the shape of the data and the `APIs` we
expose. Introducing variations in our logic with no guarding rails is often
difficult, find a subject matter expert, your compiler.
