Branded Types in Typescript

This is not an “official” feature of Typescript. This is a cool workaround that people use to enforce more specific usage of generic types, such as string or number.

For example, you have this block of code:


type User = {
    name: string;
    id: string;
}

declare function getHeightOfuser(id: string): number;

// ...

const user = {name: "Johnny", id: "1234abc"}
getHeightOfuser(user.id); // <- probably right
getHeightOfuser(user.name); //<- probably wrong

Even though we can pass any old string to getHeightOfuser, what we REALLY want is to make sure that we’re sending a user id to that function.

Instead, we can do this:

type UserId = string & {__brand: "User"};

type User = {
  id: UserId;
  name: string;
}

declare function getHeightOfuser(id: UserId): number;

const user: User = {name: "Johnny", id: "1234abc" as UserId};

getHeightOfuser(user.id);
getHeightOfuser(user.name);

Example on Typescript playground: link

So, we get a way to enforce that ONLY user’s id is passed in. A bit stringent if you ask me but I’m chill with it.

Also, this only works if you NEVER touch the __brand attribute. You don’t have to define that attribute, but it needs to exist in typescript world.

Alternative Method

Instead of the __brand property, you can also use a Brand generic, as shown by Matt Pocock in this tweet thread.

declare const brand: unique symbol;

type Brand<T, TBrand extends string> = T & {[brand]: TBrand};
type Password = Brand<string, "Password">;

const bro = "yoyoyoooo";

const validatePassword = (unsafe: string): unsafe is Password => {
  return unsafe.length < 8
}

const writePassword = (safe: Password) => {
  // write to db
}

validatePassword(bro)
if(validatePassword(bro)) {
  writePassword(bro)
}
end of storey Last modified: