An open API service indexing awesome lists of open source software.

https://github.com/scalarhq/nominal

🔒 The new way to do types in typescript.
https://github.com/scalarhq/nominal

type-safety types typescript

Last synced: 6 months ago
JSON representation

🔒 The new way to do types in typescript.

Awesome Lists containing this project

README

          

# Nominal
The right way to do types in typescript.

## Installation

```bash
npm install nominal-types

yarn install nominal-types

pnpm install nominal-types
```

# Usage

## Basic
The most immediate benefit of nominal types is preventing confusion between two types. In regular Typescript
you run into this problem:
```ts
type Minutes = number
type Seconds = number
const minutesToSeconds = (minutes: Minutes) => minutes * 60

const seconds: Seconds = 420
// uh-oh, we can use Minutes and Seconds interchangeably
minutesToSeconds(seconds)
```

Nominal types solve this problem
```ts
import { Nominal, nominal } from 'nominal-types';

type Minutes = Nominal<'Minutes', number>;
type Seconds = Nominal<'Seconds', number>;

const minutesToSeconds = (minutes: Minutes) => minutes * 60

// You can directly type cast or use nominal.make
const seconds = nominal.make(420)
const minutes = 1337 as Minutes

// doesn't work, yay type safety
minutesToSeconds(seconds)
// does work!
minutesToSeconds(minutes)
```

## Another example
You can use nominal types to give your code even better type-safety guarantees.

This goes **beyond just type-safety**, it's a performance optimization: once you know the array is sorted, you never have to sort it again. This is enforcing that at a type level.

```typescript
type SortedArray = Nominal<'sortedArray', Array>

const sort = (arr: Array): SortedArray => arr.sort()

const binarySearch = (
sorted: SortedArray,
search: T
): number | undefined => {
/* ... */
}

const regularArray = [1, 7, 2, 3, 6, 9, 10, 4, 5]
// won't work
binarySearch(regularArray, 2)
// will work
binarySearch(sort(regularArray), 3)
```

This is also known as [Refinement types](https://en.wikipedia.org/wiki/Refinement_type)

## Composing types

We can actually make this a bit crazier, we can compose nominal types

```ts

type SortedArray = Nominal<'sortedArray', Array>

const sort = (arr: Array): SortedArray => arr.sort() as SortedArray

const nonEmpty = (arr:Array):NonEmptyArray => arr.filter(Boolean) as NonEmptyArray

type NonEmptyArray> = Nominal<'nonEmptyArray', T>;
type NonEmptySorted = NonEmptyArray>;

const binarySearch = (sorted: NonEmptySorted): T => {
let foo = sorted[0]
return foo
}

// won't work
binarySearch(regularArray)
// still won't work
binarySearch(sort(regularArray))

binarySearch(nonEmpty(sort(regularArray)))

```

## Examples

More examples in [examples folder](./examples), you can also see them typed on replit.

| Example | Link |
|-------------|-----------------------------------------------------------|
| basic | https://replit.com/@CryogenicPlanet/Nominal#basic.ts |
| sorting | https://replit.com/@CryogenicPlanet/Nominal#sort.ts |
| composing | https://replit.com/@CryogenicPlanet/Nominal#composing.ts |
| safeRecords | https://replit.com/@CryogenicPlanet/Nominal#safeRecord.ts |

## Credits

You can read more about this https://zackoverflow.dev/writing/nominal-and-refinement-types-typescript

Inspiration from [Ghosts of Departed Proofs (Functional Pearl)](https://kataskeue.com/gdp.pdf)