Safer types on TypeScript
Tags: typescript, types, intermediate
Published: 2022-03-11 00:00:00

---
title: Safer types on TypeScript
published: true
description: You can use slightly different types than you would normally use to get stricter assurances on your data.
tags: ["typescript", "types", "intermediate"]
canonical_url: https://ivanalejandro0.com/blog/safer-types-on-typescript/
---
## Nominal typing on TypeScript

You can use "nominal typing" for a clearer and safer code, as well as to potentially reduce the need of data validation on multiple places.

TypeScript has type compatibility based on "structural typing" but we can get some kind of "nominal typing" with not much effort.

You can read more about TypeScript's type compatibility on [their handbook](https://www.typescriptlang.org/docs/handbook/type-compatibility.html).

Please read the references section on the bottom for information about "structural typing", "nominal typing" and the different approaches that can be used.


## Example

Consider this person renaming function:
```ts
function renamePerson(id: string, newName: string): boolean {
```

The `id` and `newName` arguments can be any `string`, they don't have to be valid id/name respectively (for whatever definition of valid we use on our app).

Because of that, the safest thing to do is to actually validate them as soon as we enter the function.

We would also have validation for the person name somewhere on the UI so we can give feedback to the user if there's a wrong name being used.

In order to make sure that the `renamePerson` function actually receives valid data we can do something like this:

```ts
function renamePerson(id: PersonId, newName: PersonName): boolean {
```

Note that `id` and `newName` now have their own type.

How should we define those types?

We can't use something like `type PersonName = string` because, due to TypeScript type compatibility being based on structural typing, you could send `string`s for those parameters and that'll just work without complaints from the compiler.

There are several ways of doing this, I'll just pick the simplest one to explain the concept, once you're familiar with the idea you can choose from other variations.

We create a custom type with something specific to it to simulate "nominal typing". Here's an example:

```ts
// intersect string with something specific to prevent a structural typing match
type PersonName = string & { __brand: "valid person name" };
```

Let's use it:
```ts
function sayHello(name: PersonName) { /* ... */ }
sayHello("Alice");

// Argument of type 'string' is not assignable to parameter of type 'PersonName'.
//   Type 'string' is not assignable to type '{ __brand: "valid person name"; }'.(2345)
```

Now, our `PersonName` typed data is not "structurally compatible" with a `string`. We have to use our own type.

```ts
// any variable of type PersonName must be created here
// that way we guarantee that validation happens
function getPersonName(personName: string): PersonName | undefined {
  if (isValidPersonName(personName)) return;
  return personName as PersonName;
}

function sayHello(name: PersonName) { /* ... */ }
const name = getPersonName("Alice");
if (name !== undefined) sayHello(name);
```

If we only create a `PersonName` using this "constructor", and the validation is done properly, we can be certain that everywhere we see a `PersonName` is going to be valid.

We could validate a `string` as soon as we receive it from a user or from an API, and then create a `PersonName`, from that point on we can be certain that we are dealing with valid data.


## Playground
Here's a TypeScript playground with some example code: [typescript playground](https://www.typescriptlang.org/play?#code/PTAEEsDsBcFMCcDOsDG1SOvKBzUB3caACwwHsBbWE3DAB1XADNwVRozQ75YA3WGKACGGLAFc0Y+EIA27AJ51aFIdBTEAsAChoi2KAAKCRGUgA5IVVABeUdkh4AZKADeoAPruARtMgATAC5QACJeWXA-LmNTUEhLWGDQAF8Abm1tJjFINHAYxCF5AAlYGRkyAAo4qiCjJFMLKgBKV21QUBRTExlYADoynHLg4tLORIBqWPjGtK0k9K18opKywYBBGVYE6baQdmJwRAJwUtAccH5QeTIxYUhQBHgyeG1d1fgcMSpBMiYFBlAAOSYew4AEQQ6QMjoISIRDgHBxLzddicOhCaRUODwUA-P76AG1EzmeIAnovMBtAAqekBwNwYIOsShwlh8MRyI4eMBbk8PiE-iCoXCkQYdTuVQSKWSpPKACYAMwAFgArI15plstBcncDgA1YWE+rxSrxIJ0hyNIJeMhkbr8lpaNo8aBScXxUAAQmstmCwRmcy05Nu8lAYWwQiR+lxun+huJVgoYkwoC8+hQPFUsEixAQsCDJFUBAKBH0H3R-Lg+gL6DCGz8qm1oGIQjoDEgiAyWRyMRw1DjDVg5VFRIHZqwuEthmi8f0AB9QFk-LAWJAsw62sxQOUPXqDdOB0P91Nms7XTMndRXVExQOWVOb-F-fMOu30MOjVZbL3oP3jcEAEJkF4wTTNom6Hg+VherYi7LlAWbNIswwrO+M6gYGWgvsmUC1hEv6fqcfZHlQgwgTM4E4XukH6NBC7+HBq5+IhBTIRUlF1vhsDTEAA)


## References
Here are some articles where You can read more about nominal typing:
* https://michalzalecki.com/nominal-typing-in-typescript/
* https://betterprogramming.pub/nominal-typescript-eee36e9432d2
* https://levelup.gitconnected.com/nominal-typing-in-typescript-c712e7116006

There's also TypeScript's handbook:
* https://www.typescriptlang.org/docs/handbook/type-compatibility.html

An example of this on TypeScript's playground:
* https://www.typescriptlang.org/play#example/nominal-typing

Some relevant Stack Overflow answers:
* https://stackoverflow.com/a/50521248/687989
* https://stackoverflow.com/a/50528847/687989

## P.S. about people names
I used people names for my example because it's easy to grasp for an example, but remember that defining what a valid name is is not straigforward. See the "Falsehoods Programmers Believe About Names" article:
* https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
* https://shinesolutions.com/2018/01/08/falsehoods-programmers-believe-about-names-with-examples/