Skip to content

kpudlik/ts-nominal-types

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 

Repository files navigation

ts-nominal-types

"If it walks like a duck and it quacks like a duck, then it must be a duck."

Set of guards to mimic nominal types and type kinds in TypeScript

TypeScript and duck-typing

TypeScript is based on duck-typing (aka structural typing). Consider following example:

interface A {
  prop: string
}

interface B {
  prop: string
}

function doSthWithA(a: A) {
  console.log(a)
}

const b: B = {
  prop: 'b'
}

doSthWithA(b) // OK!

For TypeScript these interfaces are equal because of same structures. Such behavior has a lot of advantages but sometimes it is not what we want:

type Age = number
type Speed = number

function isAdult(age: Age): boolean {
  return age >= 18
}

const speed: Speed = 100

isAdult(speed) // OK!

There are cases where you want to distinguish two aliases of number type. Another example can be entities with unique ids:

type UserId = number
class User {
  public readonly id: UserId
}

type FooId = number
class Foo {
  public readonly id: FooId
}

function getSthByUserId(userId: UserId): any {/* ... */}

const foo = new Foo()

getSthByUserId(foo.id) // OK!

If such case the logic is not type safe enough because we want to restrict our method to get only special type of number which is UserId.

String<K>, Number<K> - built in types with kinds

import { Number } from 'ts-nominal-types'

type Age = Number<'Age'>
type Speed = Number<'Speed'>

function isAdult(age: Age): boolean {
  return age >= 18
}

const speed: Speed = 100

isAdult(speed) // Compile error! Speed is not assignable to Age!
type UserId = Number<User> // class can also be a type-kind
class User {
  public readonly id: UserId
}

type FooId = Number<Foo>
class Foo {
  public readonly id: FooId
}

function getSthByUserId(userId: UserId): any {/* ... */}

const foo = new Foo()

getSthByUserId(foo.id) // Compile error! FooId is not assignable to UserId

NominalTypeGuard<T, K> - generic type for providing type-kind.

type MyUniqueBooleanA = NominalTypeGuard<boolean, 'A'>
type MyUniqueBooleanB = NominalTypeGuard<boolean, 'B'>

const a: MyUniqueBooleanA = true as MyUniqueBooleanB // Compile error! MyUniqueBooleanA is not assignable to MyUniqueBooleanB

About

Set of guards to mimic nominal types in TypeScript

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published