@prosopo/ts-brand
TypeScript icon, indicating that this package has built-in type declarations

1.0.2 • Public • Published

Brand your TypeScript types!

See our tutorial for more info on what branding is and why you'd want to use it.

Nominal types example

type A = {
    x: number
    y: boolean
    z: string
}

type B = {
    x: number
    y: boolean
    z: string
}

Type A and B are equal in the eyes of TypeScript.

const fn = (a: A) => {
    console.log('do something with A')
}

const obj: B = {
    x: 1,
    y: true,
    z: 'hello',
}

fn(obj) // absolutely fine, even though fn accepts types of A and obj is of type B!

Let's brand A

type ABranded = Brand<A, 'A'> // {
//     x: number;
//     y: boolean;
//     z: string;
// } & {
//     [brandKey]: "A";
// }
const fn = (a: ABranded) => {
    console.log('do something with A')
}

const obj: B = {
    x: 1,
    y: true,
    z: 'hello',
}

fn(obj) // Now this doesn't work, cannot accept any type other than ABranded!

Now the function only accepts a set type.

Mapped type example

Using ABranded from before, we can do conditional typing.

type IsA<T> = T extends ABranded ? true : false

type x = IsA<ABranded> // true
type y = IsA<B> // false

Obviously this is a simple example, but branding enables conditional typing. This would be impossible using regular types in TypeScript, because type B is seen as equal to type A. Read our blog post for a more detailed explanation.

Classes & Instances

You can brand instances of a class or the class itself (which will produce branded instances).

class Dog {
    constructor(public name: string) {}
}

const DogBranded = brandClass(Dog, 'Dog') // adds the 'Dog' brand, making a new type

const dog = new DogBranded('Spot') // ok, of type DogBranded

Conditional typing can now be done using classes.

Or to brand an instance:

const dogBranded = brand(new Dog(), 'Dog') of type Dog & { [brandKey]: 'Dog' }

Unbranding

Simply do the inverse to get back to the original type.

const DogUnbranded = unbrandClass(DogBranded) // same as the Dog class

const dog = new DogUnbranded('Spot') // ok, of type Dog

Or to unbrand an instance:

const dogUnbranded = unbrand(dog) // of type Dog

Get brand

Given a unknown branded value

const b = getBrand(dog) // b is of type 'Dog'

No brand set:

const b = getBrand(someValue) // b is of type '' - i.e. no brand

Important note

Branding is type only, so only available at compile time! This means using things like getBrand() will give you the brand as a type, not as a runtime variable!

Readme

Keywords

none

Package Sidebar

Install

npm i @prosopo/ts-brand

Weekly Downloads

3

Version

1.0.2

License

Apache-2.0

Unpacked Size

15.3 kB

Total Files

13

Last publish

Collaborators

  • hughparry
  • goastler
  • christaylor