0.13.0 • Published 2 days ago

@sidewinder/type v0.13.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 days ago

Overview

Sidewinder Types is an extended version of the TypeBox type library. It provides built in support for Uint8Array. It also encodes serializable runtime reflection data allowing remote systems to introspect and reconstruct associative types when schemas are exchanged over the wire. For additional information on TypeBox see the official documentation located here. For validation of the schemas built with this library, refer to the Sidewinder Validation package located here

License MIT

Contents

Install

$ npm install @sidewinder/type

Usage

The following demonstrates general usage.

import { Static, Type } from '@sidewinder/type'

//--------------------------------------------------------------------------------------------
//
// Let's say you have the following type ...
//
//--------------------------------------------------------------------------------------------

type T = {
  id: string
  name: string
  timestamp: number
}

//--------------------------------------------------------------------------------------------
//
// ... you can express this type in the following way.
//
//--------------------------------------------------------------------------------------------

const T = Type.Object({
  // const T = {
  id: Type.String(), //   type: 'object',
  name: Type.String(), //   properties: {
  timestamp: Type.Integer(), //      id: {
}) //         type: 'string'
//      },
//      name: {
//         type: 'string'
//      },
//      timestamp: {
//         type: 'integer'
//      }
//   },
//   required: [
//      "id",
//      "name",
//      "timestamp"
//   ]
// }

//--------------------------------------------------------------------------------------------
//
// ... then infer back to the original static type this way.
//
//--------------------------------------------------------------------------------------------

type T = Static<typeof T> // type T = {
//    id: string,
//    name: string,
//    timestamp: number
// }

//--------------------------------------------------------------------------------------------
//
// ... then use the type both as JSON schema and as a TypeScript type.
//
//--------------------------------------------------------------------------------------------

function receive(value: T) {
  // ... as a Type

  if (JSON.validate(T, value)) {
    // ... as a Schema
    // ok...
  }
}

Types

The following table outlines the Sidewinder Type mappings between TypeScript and JSON schema.

┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ Sidewinder Type                │ TypeScript                  │ JSON Schema                    │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Any()           │ type T = any                │ const T = { }                  │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Unknown()       │ type T = unknown            │ const T = { }                  │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.String()        │ type T = string             │ const T = {                    │
│                                │                             │    type: 'string'              │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Number()        │ type T = number             │ const T = {                    │
│                                │                             │    type: 'number'              │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Integer()       │ type T = number             │ const T = {                    │
│                                │                             │    type: 'integer'             │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Boolean()       │ type T = boolean            │ const T = {                    │
│                                │                             │    type: 'boolean'             │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Null()          │ type T = null               │ const T = {                    │
│                                │                             │    type: 'null'                │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.RegEx(/foo/)    │ type T = string             │ const T = {                    │
│                                │                             │    type: 'string',             │
│                                │                             │    pattern: 'foo'              │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Literal(42)     │ type T = 42                 │ const T = {                    │
│                                │                             │    const: 42                   │
│                                │                             │    type: 'number'              │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Array(          │ type T = number[]           │ const T = {                    │
│    Type.Number()               │                             │    type: 'array',              │
│ )                              │                             │    items: {                    │
│                                │                             │      type: 'number'            │
│                                │                             │    }                           │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({        │ type T = {                  │ const T = {                    │
│   x: Type.Number(),            │    x: number,               │   type: 'object',              │
│   y: Type.Number()             │    y: number                │   properties: {                │
│ })                             │ }                           │      x: {                      │
│                                │                             │        type: 'number'          │
│                                │                             │      },                        │
│                                │                             │      y: {                      │
│                                │                             │        type: 'number'          │
│                                │                             │      }                         │
│                                │                             │   },                           │
│                                │                             │   required: ['x', 'y']         │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Tuple([         │ type T = [number, number]   │ const T = {                    │
│   Type.Number(),               │                             │    type: 'array',              │
│   Type.Number()                │                             │    items: [                    │
│ ])                             │                             │       {                        │
│                                │                             │         type: 'number'         │
│                                │                             │       }, {                     │
│                                │                             │         type: 'number'         │
│                                │                             │       }                        │
│                                │                             │    ],                          │
│                                │                             │    additionalItems: false,     │
│                                │                             │    minItems: 2,                │
│                                │                             │    maxItems: 2,                │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ enum Foo {                     │ enum Foo {                  │ const T = {                    │
│   A,                           │   A,                        │   anyOf: [{                    │
│   B                            │   B                         │     type: 'number',            │
│ }                              │ }                           │     const: 0                   │
│                                │                             │   }, {                         │
│ const T = Type.Enum(Foo)       │ type T = Foo                │     type: 'number',            │
│                                │                             │     const: 1                   │
│                                │                             │   }]                           │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.KeyOf(          │ type T = keyof {            │ const T = {                    │
│   Type.Object({                │   x: number,                │    enum: ['x', 'y'],           │
│     x: Type.Number(),          │   y: number                 │    type: 'string'              │
│     y: Type.Number()           │ }                           │ }                              │
│   })                           │                             │                                │
│ )                              │                             │                                │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Union([         │ type T = string | number    │ const T = {                    │
│   Type.String(),               │                             │    anyOf: [{                   │
│   Type.Number()                │                             │       type: 'string'           │
│ ])                             │                             │    }, {                        │
│                                │                             │       type: 'number'           │
│                                │                             │    }]                          │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Intersect([     │ type T = {                  │ const T = {                    │
│    Type.Object({               │    x: number                │    allOf: [{                   │
│       x: Type.Number()         │ } & {                       │       type: 'object',          │
│    }),                         │    y: number                │       properties: {            │
│    Type.Object({               │ }                           │          x: {                  │
│       y: Type.Number()         │                             │            type: 'number'      │
│   })                           │                             │          }                     │
│ })                             │                             │       },                       │
│                                │                             │       required: ['x']          │
│                                │                             │    }, {                        │
│                                │                             │       type: 'object',          │
│                                │                             │       properties: {            │
│                                │                             │          y: {                  │
│                                │                             │            type: 'number'      │
│                                │                             │          }                     │
│                                │                             │       },                       │
│                                │                             │       required: ['y']          │
│                                │                             │    }]                          │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Record(         │ type T = {                  │ const T = {                    │
│    Type.String(),              │    [key: string]: number    │    type: 'object',             │
│    Type.Number()               │ }                           │    patternProperties: {        │
│ )                              │                             │      '^.*$': {                 │
│                                │                             │         type: 'number'         │
│                                │                             │      }                         │
│                                │                             │    }                           │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Partial(        │ type T = Partial<{          │ const T = {                    │
│    Type.Object({               │    x: number,               │   type: 'object',              │
│         x: Type.Number(),      │    y: number                │   properties: {                │
│         y: Type.Number()       | }>                          │     x: {                       │
│    })                          │                             │        type: 'number'          │
│ )                              │                             │     },                         │
│                                │                             │     y: {                       │
│                                │                             │        type: 'number'          │
│                                │                             │     }                          │
│                                │                             │   }                            │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Required(       │ type T = Required<{         │ const T = {                    │
│    Type.Object({               │    x?: number,              │   type: 'object',              │
│       x: Type.Optional(        │    y?: number               │   properties: {                │
│          Type.Number()         | }>                          │     x: {                       │
│       ),                       │                             │        type: 'number'          │
│       y: Type.Optional(        │                             │     },                         │
│          Type.Number()         │                             │     y: {                       │
│       )                        │                             │        type: 'number'          │
│    })                          │                             │     }                          │
│ )                              │                             │   },                           │
│                                │                             │   required: ['x', 'y']         │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Pick(           │ type T = Pick<{             │ const T = {                    │
│    Type.Object({               │    x: number,               │   type: 'object',              │
│       x: Type.Number(),        │    y: number                │   properties: {                │
│       y: Type.Number(),        | }, 'x'>                     │     x: {                       │
│     }), ['x']                  │                             │        type: 'number'          │
│ )                              │                             │     }                          │
│                                │                             │   },                           │
│                                │                             │   required: ['x']              │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Omit(           │ type T = Omit<{             │ const T = {                    │
│    Type.Object({               │    x: number,               │   type: 'object',              │
│       x: Type.Number(),        │    y: number                │   properties: {                │
│       y: Type.Number(),        | }, 'x'>                     │     y: {                       │
│     }), ['x']                  │                             │        type: 'number'          │
│ )                              │                             │     }                          │
│                                │                             │   },                           │
│                                │                             │   required: ['y']              │
│                                │                             │ }                              │
│                                │                             │                                │
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘

Modifiers

Sidewinder Types provides modifiers that can be applied to an objects properties. This allows for optional and readonly to be applied to that property. The following table illustates how they map between TypeScript and JSON Schema.

┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ Sidewinder Type                │ TypeScript                  │ JSON Schema                    │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({        │ type T = {                  │ const T = {                    │
│   name: Type.Optional(         │    name?: string,           │   type: 'object',              │
│      Type.String(),            │ }                           │   properties: {                │
│   )                            │                             │      name: {                   │
│ })  	                         │                             │        type: 'string'          │
│                                │                             │      }                         │
│                                │                             │   }                            │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({        │ type T = {                  │ const T = {                    │
│   name: Type.Readonly(         │    readonly name: string,   │   type: 'object',              │
│      Type.String(),            │ }                           │   properties: {                │
│   )                            │                             │      name: {                   │
│ })  	                         │                             │        type: 'string'          │
│                                │                             │      }                         │
│                                │                             │   },                           │
│                                │                             │   required: ['name']           │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({        │ type T = {                  │ const T = {                    │
│   name: Type.ReadonlyOptional( │    readonly name?: string,  │   type: 'object',              │
│      Type.String(),            │ }                           │   properties: {                │
│   )                            │                             │      name: {                   │
│ })  	                         │                             │        type: 'string'          │
│                                │                             │      }                         │
│                                │                             │   }                            │
│                                │                             │ }                              │
│                                │                             │                                │
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘

Options

You can pass additional JSON schema options on the last argument of any given type. The following are some examples.

// string must be an email
const T = Type.String({ format: 'email' })

// number must be a multiple of 2
const T = Type.Number({ multipleOf: 2 })

// array must have at least 5 integer values
const T = Type.Array(Type.Integer(), { minItems: 5 })

Generic Types

Generic types can be created using functions. The following creates a generic Nullable<T> type.

import { Type, Static, TSchema } from '@sidewinder/type'

// type Nullable<T> = T | null

const Nullable = <T extends TSchema>(type: T) => Type.Union([type, Type.Null()])

const T = Nullable(Type.String()) // const T = {
//   "anyOf": [{
//      type: 'string'
//   }, {
//      type: 'null'
//   }]
// }

type T = Static<typeof T> // type T = string | null

const U = Nullable(Type.Number()) // const U = {
//   "anyOf": [{
//      type: 'number'
//   }, {
//      type: 'null'
//   }]
// }

type U = Static<typeof U> // type U = number | null

Reference Types

Types can be referenced with Type.Ref(...). To reference a type, the target type must specify an $id.

const T = Type.String({ $id: 'T' }) // const T = {
//    $id: 'T',
//    type: 'string'
// }

const R = Type.Ref(T) // const R = {
//    $ref: 'T'
// }

It can sometimes be helpful to organize shared referenced types under a common namespace. The Type.Namespace(...) function can be used to create a shared definition container for related types. The following creates a Math3D container and a Vertex structure that references types in the container.

const Math3D = Type.Namespace(
  {
    //  const Math3D = {
    Vector4: Type.Object({
      //    $id: 'Math3D',
      x: Type.Number(), //    $defs: {
      y: Type.Number(), //      Vector4: {
      z: Type.Number(), //        type: 'object',
      w: Type.Number(), //        properties: {
    }), //          x: { type: 'number' },
    Vector3: Type.Object({
      //          y: { type: 'number' },
      x: Type.Number(), //          z: { type: 'number' },
      y: Type.Number(), //          w: { type: 'number' }
      z: Type.Number(), //        },
    }), //        required: ['x', 'y', 'z', 'w']
    Vector2: Type.Object({
      //      },
      x: Type.Number(), //      Vector3: {
      y: Type.Number(), //        type: 'object',
    }), //        properties: {
  },
  { $id: 'Math3D' },
) //          x: { 'type': 'number' },
//          y: { 'type': 'number' },
//          z: { 'type': 'number' }
//        },
//        required: ['x', 'y', 'z']
//      },
//      Vector2: {
//        type: 'object',
//        properties: {
//          x: { 'type': 'number' },
//          y: { 'type': 'number' },
//        },
//        required: ['x', 'y']
//      }
//    }
//  }

const Vertex = Type.Object({
  //  const Vertex = {
  position: Type.Ref(Math3D, 'Vector4'), //    type: 'object',
  normal: Type.Ref(Math3D, 'Vector3'), //    properties: {
  uv: Type.Ref(Math3D, 'Vector2'), //      position: { $ref: 'Math3D#/$defs/Vector4' },
}) //      normal: { $ref: 'Math3D#/$defs/Vector3' },
//      uv: { $ref: 'Math3D#/$defs/Vector2' }
//    },
//    required: ['position', 'normal', 'uv']
//  }

Recursive Types

Recursive types can be created with the Type.Rec(...) function. The following creates a Node type that contains an array of inner Nodes. Note that due to current restrictions on TypeScript inference, it is not possible for Sidewinder Types to statically infer for recursive types. Sidewinder Types will infer the inner recursive type as any.

const Node = Type.Rec(
  (Self) =>
    Type.Object({
      // const Node = {
      id: Type.String(), //   $id: 'Node',
      nodes: Type.Array(Self), //   $ref: 'Node#/$defs/self',
    }),
  { $id: 'Node' },
) //   $defs: {
//     self: {
//       type: 'object',
//       properties: {
//         id: {
//           type: 'string'
//         },
//         nodes: {
//            type: 'array',
//            items: {
//              $ref: 'Node#/$defs/self'
//            }
//         }
//      }
//    }
// }

type Node = Static<typeof Node> // type Node = {
//   id: string
//   nodes: any[]
// }

function visit(node: Node) {
  for (const inner of node.nodes) {
    visit(inner as Node) // Assert inner as Node
  }
}

Extended Types

In addition to JSON schema types, Sidewinder Types provides several extended types that allow for function and constructor types to be composed. These additional types are not valid JSON Schema and will not validate using typical JSON Schema validation. However, these types can be used to frame JSON schema and describe callable interfaces that may receive JSON validated data. These types are as follows.

┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ Sidewinder Type                │ TypeScript                  │ Extended Schema                │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Constructor([   │ type T = new (              │ const T = {                    │
│    Type.String(),              │  arg0: string,              │   type: 'constructor'          │
│    Type.Number(),              │  arg1: number               │   arguments: [{                │
│ ], Type.Boolean())             │ ) => boolean                │      type: 'string'            │
│                                │                             │   }, {                         │
│                                │                             │      type: 'number'            │
│                                │                             │   }],                          │
│                                │                             │   returns: {                   │
│                                │                             │      type: 'boolean'           │
│                                │                             │   }                            │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Function([      │ type T = (                  │ const T = {                    │
|    Type.String(),              │  arg0: string,              │   type : 'function',           │
│    Type.Number(),              │  arg1: number               │   arguments: [{                │
│ ], Type.Boolean())             │ ) => boolean                │      type: 'string'            │
│                                │                             │   }, {                         │
│                                │                             │      type: 'number'            │
│                                │                             │   }],                          │
│                                │                             │   returns: {                   │
│                                │                             │      type: 'boolean'           │
│                                │                             │   }                            │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Promise(        │ type T = Promise<string>    │ const T = {                    │
│    Type.String()               │                             │   type: 'promise',             │
│ )                              │                             │   item: {                      │
│                                │                             │      type: 'string'            │
│                                │                             │   }                            │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Undefined()     │ type T = undefined          │ const T = {                    │
│                                │                             │   type: 'undefined'            │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Uint8Array()    │ type T = Uint8Array         │ const T = {                    │
│                                │                             │   type: 'object',              │
│                                │                             │   specialized: 'Uint8Array'    │
│                                │                             │ }                              │
│                                │                             │                                │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Void()          │ type T = void               │ const T = {                    │
│                                │                             │   type: 'null'                 │
│                                │                             │ }                              │
│                                │                             │                                │
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
0.13.0

2 days ago

0.12.10

10 months ago

0.12.11

10 months ago

0.12.12

10 months ago

0.12.14

10 months ago

0.12.15

10 months ago

0.12.8

11 months ago

0.12.9

11 months ago

0.12.7

11 months ago

0.12.6

11 months ago

0.12.5

1 year ago

0.11.0

2 years ago

0.10.14

2 years ago

0.11.1

2 years ago

0.10.15

2 years ago

0.11.2

2 years ago

0.11.3

2 years ago

0.11.4

2 years ago

0.10.10

2 years ago

0.11.5

2 years ago

0.10.11

2 years ago

0.11.6

1 year ago

0.10.12

2 years ago

0.10.13

2 years ago

0.10.9

2 years ago

0.10.1

2 years ago

0.12.0

1 year ago

0.10.2

2 years ago

0.12.1

1 year ago

0.10.3

2 years ago

0.12.2

1 year ago

0.10.4

2 years ago

0.12.3

1 year ago

0.10.5

2 years ago

0.12.4

1 year ago

0.10.6

2 years ago

0.10.7

2 years ago

0.10.8

2 years ago

0.10.0

2 years ago

0.9.2

2 years ago

0.9.1

2 years ago

0.8.85

2 years ago

0.8.84

2 years ago

0.8.81

2 years ago

0.8.80

2 years ago

0.8.83

2 years ago

0.8.82

2 years ago

0.8.78

2 years ago

0.8.79

2 years ago

0.9.0

2 years ago

0.8.77

2 years ago

0.8.74

2 years ago

0.8.73

2 years ago

0.8.76

2 years ago

0.8.75

2 years ago

0.8.70

2 years ago

0.8.72

2 years ago

0.8.71

2 years ago

0.8.67

2 years ago

0.8.66

2 years ago

0.8.69

2 years ago

0.8.68

2 years ago

0.8.63

2 years ago

0.8.62

2 years ago

0.8.65

2 years ago

0.8.64

2 years ago

0.8.61

2 years ago

0.8.60

2 years ago

0.8.59

2 years ago

0.8.56

2 years ago

0.8.58

2 years ago

0.8.57

2 years ago

0.8.45

2 years ago

0.8.44

2 years ago

0.8.47

2 years ago

0.8.46

2 years ago

0.8.49

2 years ago

0.8.48

2 years ago

0.8.55

2 years ago

0.8.52

2 years ago

0.8.51

2 years ago

0.8.54

2 years ago

0.8.53

2 years ago

0.8.50

2 years ago

0.8.43

2 years ago

0.8.42

2 years ago

0.8.41

2 years ago

0.8.40

2 years ago

0.8.39

2 years ago

0.8.38

2 years ago

0.8.37

2 years ago

0.8.36

2 years ago

0.8.35

2 years ago

0.8.34

2 years ago

0.8.33

2 years ago

0.8.32

2 years ago

0.8.31

2 years ago

0.8.30

2 years ago