Skip to content

hasparus/nom-ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nom-ts

TypeScript helpers for nominal typing


Problem

  • 😀 Nominal types are pretty useful.
  • 😞 TypeScript doesn't support nominal types (yet?).

Great! This is easy to fix right? The simple trick below solves the problem.

declare const __brand: unique symbol;

declare const __type: unique symbol;

interface __Brand<T, S extends string> {
  [__brand]: S;
  [__type]: T;
}

export type Brand<T, S extends string> = T & __Brand<T, S>;

But then we encounter some more happy little problems.

type PlayerId = Brand<number, 'PlayerId'>;
type PlayerScores = Record<PlayerId, number>;

const myId = 1337 as PlayerId;
const scores: PlayerScores = { [myId]: 9001 };

// Element implicitly has an 'any' type because expression
// of type 'Brand<number, "PlayerId">' can't be used to
// index type 'Record<Brand<number, "PlayerId">, number>'.
// ts(7053)
const myScore = scores[myId];

nom-ts aims to address these problems.

Goals

  • 🦅 no dependencies
  • 🦥 all branded types in nom-ts are assignable to their underlying type without any additional syntax

The rest of the code

interface __Flavor<T, S extends string> {
  [__brand]?: S;
  /**
   * Intersection is distributive over union, but we don't want to "lose" information about T.
   * `__type?: T` will be useful for Unbrand.
   */
  [__type]?: T;
}

export type Flavor<T, S extends string> = T & __Flavor<T, S>;

export type Unbrand<T> = T extends Flavor<infer X, any> ? X : T;

export const Unbrand = <T extends any>(x: T) => x as Unbrand<T>;

export type Dict<K extends PropertyKey, V> = Record<Unbrand<K>, V>;

Links


Credits

This project was bootstrapped with TSDX.

About

🤓 TypeScript nominal-typing helpers

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published