Typescript Nominal Type: The Right Way

Acid Coder
2 min readJun 28, 2023

In case you don’t know what is nominal typing

Typescript is a structural typing language, but it is possible to mimic the behavior of nominal typing language, very closely.

We start with the easiest method:

type AB = { a:number, b:number, x:never}
type BA = { a:number, b:number, y:never}
const ab:AB = {a:1, b:2}
const ba:BA = ab // error !!

playground

It works

BUT THIS IS NOT GOOD ENOUGH !!

Today we are going to learn how to create a proper nominal type in Typescript

But first let us review why the first method is not good enough.

First, it use type assertion. Avoid type assertion if possible, personally I only assert type when I am creating wrappers.

Second, users can access the extra property: brand via static typing. In this case, our brand is ab.x and ba.y.

As a craftsman, we don’t want that to happen

Solution

declare class AB_Class { 
protected x?:never
}
declare class BA_Class {
protected x?:never
}
interface AB extends AB_Class{
a:number, b:number
}
interface BA extends BA_Class{
a:number, b:number
}
const ab:AB = {a:1, b:2}
const ba:BA = ab // error !!

playground

The key is to use class and declare optional protected brands with never as type.

The brands name dont have to be the same name, you can assign different names.

  1. no type assertion
  2. users cannot access brand

now, you may ask, why protected, why not private ?

This is because, take this example

declare class AB_Class { 
private x?:never
}

after tsc compilation, the output is

declare class AB_Class { 
private x?
}

do you notice the difference?

Typescript strips away the type of private and fails type assertion

so please stick with protected

--

--

Acid Coder

Typescript Zombie. Youtube Pikachu On Acid. (Unrelated to programming but by watching it you become a good developer overnight)