Skip to content

Commit ec0eec2

Browse files
committed
docs(nominal-typebox): add readme
Signed-off-by: Andres Correa Casablanca <[email protected]>
1 parent 913b01c commit ec0eec2

File tree

3 files changed

+158
-1
lines changed

3 files changed

+158
-1
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# @coderspirit/nominal-typebox
2+
3+
[![NPM version](https://img.shields.io/npm/v/@coderspirit/nominal-typebox.svg?style=flat)](https://www.npmjs.com/package/@coderspirit/nominal-typebox)
4+
[![TypeScript](https://badgen.net/npm/types/@coderspirit/nominal-typebox)](http://www.typescriptlang.org/)
5+
[![License](https://badgen.net/npm/license/@coderspirit/nominal-typebox)](https://opensource.org/licenses/MIT)
6+
[![npm downloads](https://img.shields.io/npm/dm/@coderspirit/nominal-typebox.svg?style=flat)](https://www.npmjs.com/package/@coderspirit/nominal-typebox)
7+
[![Known Vulnerabilities](https://snyk.io//test/github/Coder-Spirit/nominal/badge.svg?targetFile=@coderspirit/nominal-typebox/package.json)](https://snyk.io//test/github/Coder-Spirit/nominal?targetFile=@coderspirit/nominal-typebox/package.json)
8+
[![Security Score](https://snyk-widget.herokuapp.com/badge/npm/@coderspirit%2Fnominal-typebox/badge.svg)](https://snyk.io/advisor/npm-package/@coderspirit/nominal-typebox)
9+
10+
`Nominal-Typebox` brings [nominal typing](https://en.wikipedia.org/wiki/Nominal_type_system)
11+
capabilities to [Typebox](https://github.com/sinclairzx81/typebox) schema
12+
definitions by leveraging [Nominal](https://github.com/Coder-Spirit/nominal/blob/main/%40coderspirit/nominal/README.md).
13+
14+
## Install instructions
15+
16+
### Node
17+
18+
```
19+
# With NPM
20+
npm install @sinclair/typebox
21+
npm install @coderspirit/nominal-typebox
22+
23+
# Or with PNPM
24+
pnpm add @sinclair/typebox
25+
pnpm add @coderspirit/nominal-typebox
26+
27+
# Or with Yarn:
28+
yarn add @sinclair/typebox
29+
yarn add @coderspirit/nominal-typebox
30+
```
31+
32+
## Usage instructions
33+
34+
### Typebox' Type.String -> brandedString
35+
36+
```typescript
37+
import type { FastBrand } from '@coderspirit/nominal'
38+
import { brandedString } from '@coderspirit/nominal-typebox'
39+
40+
import { Object as TBObject } from '@sinclair/typebox'
41+
import { TypeCompiler } from '@sinclair/typebox/compiler'
42+
43+
type Username = FastBrand<string, 'Username'>
44+
45+
// Use `brandedString` instead of Typebox' `Type.String`
46+
const requestSchema = TBObject({
47+
// We can pass the same options Type.String has
48+
username: brandedString<'Username'>()
49+
})
50+
const requestValidator = TypeCompiler.Compile(requestSchema)
51+
52+
const requestObject = getRequestFromSomewhere() // unknown
53+
if (!requestValidator.Check(requestObject)) {
54+
throw new Error('Invalid request!')
55+
}
56+
57+
// At this point, the type checker knows that requestObject.username is
58+
// "branded" as 'Username'
59+
60+
const username: Username = requestObject.username // OK
61+
const corruptedUserame: Username = 'untagged string' // type error
62+
```
63+
64+
### Typebox' Type.Number -> brandedNumber
65+
66+
67+
```typescript
68+
import type { FastBrand } from '@coderspirit/nominal'
69+
import { brandedNumber } from '@coderspirit/nominal-typebox'
70+
71+
import { Object as TBObject } from '@sinclair/typebox'
72+
import { TypeCompiler } from '@sinclair/typebox/compiler'
73+
74+
type Latitude = FastBrand<number, 'Latitude'>
75+
type Longitude = FastBrand<number, 'Longitude'>
76+
77+
const requestSchema = TBObject({
78+
// We can pass the same options Type.Number has
79+
latitude: brandedNumber<'Latitude'>(),
80+
longitude: brandedNumber<'Longitude'>(),
81+
})
82+
const requestValidator = TypeCompiler.Compile(requestSchema)
83+
84+
const requestObject = getRequestFromSomewhere() // unknown
85+
if (!requestValidator.Check(requestObject)) {
86+
throw new Error('Invalid request!')
87+
}
88+
89+
const latitude: Latitude = requestObject.latitude // OK
90+
const longitude: Longitude = requestObject.longitude // OK
91+
92+
const corruptedLat: Latitude = 10 // type error
93+
const corruptedLon: Longitude = 10 // type error
94+
```
95+
96+
### Typebox' Type.Integer -> brandedInteger
97+
98+
The same applies as for the two previous examples, you can use `brandedInteger`
99+
instead of Typebox' `Type.Integer`.
100+
101+
### Typebox' Type.Array -> brandedArray
102+
103+
`brandedArray` has the same signature as Typebox' `Type.Array`, except that we
104+
have to pass a "brand" string argument as its first parameter:
105+
106+
```typescript
107+
import { brandedArray } from '@coderspirit/nominal-typebox'
108+
import { String as TBString } from '@sinclair/typebox'
109+
110+
const arraySchema = brandedArray(
111+
'MyArray',
112+
// Type.Array arguments:
113+
TBString(),
114+
{ minItems: 2 }
115+
)
116+
```
117+
118+
### Typebox' Type.Object -> brandedObject
119+
120+
`brandedObject` has the same signature as Typebox' `Type.Object`, except that we
121+
have to pass a "brand" string argument as its first parameter:
122+
123+
```typescript
124+
import { brandedObject } from '@coderspirit/nominal-typebox'
125+
import { String as TBString } from '@sinclair/typebox'
126+
127+
const objectSchema = brandedObject(
128+
'MyObject',
129+
{
130+
a: TBstring(),
131+
b: TBString()
132+
},
133+
{ additionalProperties: true }
134+
)
135+
```
136+
137+
### Typebox' Type.Union -> brandedUnion
138+
139+
`brandedUnion` has the same signature as Typebox' `Type.Union`, except that we
140+
have to pass a "brand" string argument as its first parameter:
141+
142+
```typescript
143+
import { brandedUnion } from '@coderspirit/nominal-typebox'
144+
import { Literal } from '@sinclair/typebox'
145+
146+
const unionSchema = brandedUnion(
147+
'State',
148+
[Literal('on'), Literal('off')]
149+
)
150+
```

@coderspirit/nominal/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ combine *brands* or *flavors* with *properties*.
3434

3535
```
3636
# With NPM
37-
npm install @coderspirit/nominal
37+
npm install --save-dev @coderspirit/nominal
3838
3939
# Or with PNPM
4040
pnpm add --save-dev @coderspirit/nominal
@@ -43,6 +43,12 @@ pnpm add --save-dev @coderspirit/nominal
4343
yarn add --dev @coderspirit/nominal
4444
```
4545

46+
> [!TIP]
47+
> Note that if you are developing an application, it is fine to install
48+
> `@coderspirit/nominal` as a development dependency, but it might be necessary
49+
> to install it as a normal or peer dependency in case you are developing your
50+
> own libraries based on it.
51+
4652
## Brands
4753

4854
```typescript

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [@coderspirit/nominal](@coderspirit/nominal/README.md)
44
- [@coderspirit/nominal-inputs](@coderspirit/nominal-inputs/README.md)
55
- [@coderspirit/nominal-symbols](@coderspirit/nominal-symbols/README.md)
6+
- [@coderspirit/nominal-typebox](@coderspirit/nominal-typebox/README.md)
67
- [@coderspirit/safe-env](@coderspirit/safe-env/README.md)
78
- [@coderspirit/lambda-ioc](@coderspirit/lambda-ioc/README.md)
89

0 commit comments

Comments
 (0)