deno.land / x / typebox@0.32.21 / changelog / 0.26.0.md

0.26.0

Overview

TypeBox now provides "runtime conditional types" (formally the Conditional module) as standard on Type.*. Additional updates in this revision include automatic union and intersection unwrap, universal support for utility types, several ergonomic enhancements and additional options for framework integrators. This revision also carries out a number an internal refactorings to reduce the amount of submodule imports.

Revision 0.26.0 is a milestone release for the TypeBox project and requires a minor semver update.

Contents

Standard Type Builder

Revision 0.26.0 exports a new type builder called StandardType. This builder only allows for the construction JSON Schema compliant types by omitting all Extended types.

import { StandardType as Type, Static } from '@sinclair/typebox'

const T = Type.Date() // error: no such function

Automatic Unwrap for Union and Intersect

Revision 0.26.0 will automatically unwrap unions and intersections for the following cases.

const T1 = Type.Union([Type.String(), Type.Number()]) // TUnion<[TString, TNumber]>

const T2 = Type.Union([Type.String()])                // TString

const T3 = Type.Union([])                             // TNever

Intersect and Union now Compose

Revision 0.26.0 re-enables support for union and intersection type composition. These types are also made compatible with Pick, Omit, Partial, Required and KeyOf utility types.

const A = Type.Object({ type: Type.Literal('A') })
const B = Type.Object({ type: Type.Literal('B') })
const C = Type.Object({ type: Type.Literal('C') })

const Union = Type.Union([A, B, C])

const Extended = Type.Object({
  x: Type.Number(),
  y: Type.Number(),
  z: Type.Number()
})

const T = Type.Intersect([Union, Extended])          // type T = ({
                                                     //   type: "A";
                                                     //  } | {
                                                     //   type: "B";
                                                     // } | {
                                                     //   type: "C";
                                                     // }) & {
                                                     //   x: number;
                                                     //   y: number;
                                                     //   z: number;
                                                     // }

const K = Type.KeyOf(T)                              // type K = "type" | "x" | "y" | "z"

const P = Type.Pick(T, ['type', 'x'])                // type P = ({
                                                     //   type: "A";
                                                     // } | {
                                                     //   type: "B";
                                                     // } | {
                                                     //   type: "C";
                                                     // }) & {
                                                     //   x: number;
                                                     // }

const O = Type.Partial(P)                            // type O = ({
                                                     //   type?: "A" | undefined;
                                                     // } | {
                                                     //   type?: "B" | undefined;
                                                     // } | {
                                                     //   type?: "C" | undefined;
                                                     // }) & {
                                                     //   x?: number | undefined;
                                                     // }

Runtime Conditional Types

Revision 0.26.0 adds the runtime conditional types Extends, Extract and Exclude as standard.

TypeScript

type T0 = string extends number ? true : false                                                 
//   ^ false
type T1 = Extract<string | number, number>                                                     
//   ^ number
type T2 = Exclude<string | number, number>                                                     
//   ^ string

TypeBox

const T0 = Type.Extends(Type.String(), Type.Number(), Type.Literal(true), Type.Literal(false)) 
//    ^ TLiteral<false>
const T1 = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.Number())             
//    ^ TNumber
const T2 = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.Number())             
//    ^ TString<string>

Value Convert

Revision 0.26.0 adds a new Convert function to the Value.* module. This function will perform a type coercion for any value mismatched to its type if a reasonable conversion is possible.

const T = Type.Number()

const A = Value.Convert(T, '42')                     // const A: unknown = 42 - ... Convert(...) will return `unknown`

const B = Value.Check(T, A)                          // const B = true        - ... so should be checked

Error Iterator

Revision 0.26.0 now returns a ValueErrorIterator for .Errors(...). This iterator provides a utility function to obtain the first error only. To obtain all errors, continue to use for-of enumeration or array spread syntax.

const T = Type.Number()

const First = Value.Errors(T, 'foo').First()         // const First = { path: '', message: 'Expected number', ... }

const All = [...Value.Errors(T, 'foo')]              // const All = [{ path: '', message: 'Expected number', ... }]

Codegen without JIT

Revision 0.26.0 adds a .Code() function to the TypeCompiler to enable code generation without JIT evaluation.

import { TypeCompiler } from '@sinclair/typebox/compiler'

const T = Type.Object({
  x: Type.Number(),
  y: Type.Number()
})

const C = TypeCompiler.Code(T)                       // return function check(value) {
                                                     //   return (
                                                     //    (typeof value === 'object' && value !== null) &&
                                                     //    !Array.isArray(value) &&
                                                     //    typeof value.x === 'number' &&
                                                     //    Number.isFinite(value.x) &&
                                                     //    typeof value.y === 'number' &&
                                                     //    Number.isFinite(value.y)
                                                     //   )
                                                     // }

Standard Type (Not)

Revision 0.26.0 introduces the Not standard type. This type allows for the inversion of assertion logic which can be useful to narrow for broader types.

Example 1

const T = Type.Not(Type.String({ pattern: 'A|B|C' }), Type.String())

Value.Check(T, 'A')                                  // false
Value.Check(T, 'B')                                  // false
Value.Check(T, 'C')                                  // false
Value.Check(T, 'D')                                  // true

Example 2

const Even = Type.Number({ multipleOf: 2 })
const Odd  = Type.Not(Even, Type.Number())

Value.Check(Even, 0)                                  // true
Value.Check(Even, 1)                                  // false
Value.Check(Even, 2)                                  // true

Value.Check(Odd, 0)                                   // false
Value.Check(Odd, 1)                                   // true
Value.Check(Odd, 2)                                   // false

Standard Type (Composite)

Revision 0.26.0 includes a new Composite standard type. This type will combine an array of TObject[] into a TObject by taking a union of any overlapping properties.

const A = Type.Object({ type: Type.Literal('A') })

const B = Type.Object({ type: Type.Literal('B') })

const C = Type.Object({ type: Type.Literal('C'), value: Type.Number() })

const T = Type.Composite([A, B, C])                  // type T = { 
                                                     //   type: 'A' | 'B' | 'C'
                                                     //   value: number
                                                     // }

Extended Type (Symbol)

Revision 0.26.0 provides provisional support for Symbol type validation.

const T = Type.Symbol()

Value.Check(A, Symbol('Foo'))                        // true

Extended Type (BigInt)

Revision 0.26.0 provides provisional support for BigInt type validation.

const T = Type.BigInt({ minimum: 10n })

Value.Check(B, 1_000_000n)                           // true

Breaking Changes

The following are breaking changed in Revision 0.26.0

Minimum TypeScript Version

Revision 0.26.0 requires a minimum recommended TypeScript version of 4.2.3. Version 4.1.5 is no longer supported.

Intersect Schema Representation

Revision 0.26.0 changes the schema representation for Intersect. Revision 0.25.0 would construct a composite object type, in 0.26.0, Intersect is expressed as anyOf. If upgrading, consider using Type.Composite(...) to return backwards compatible representations.

Intersect 0.25.0

const T = Type.Intersect([                           // const U = {
  Type.Object({                                      //   type: 'object',
    x: Type.Number(),                                //   required: ['x', 'y'],  
  }),                                                //   properties: {  
  Type.Object({                                      //     x: {   
    y: Type.Number(),                                //       type: 'number'   
  })                                                 //     },   
])                                                   //     y: {     
                                                     //       type: 'number'   
                                                     //     }  
                                                     //   }  
                                                     // } 

Intersect 0.26.0

const T = Type.Intersect([                          // const U = {
  Type.Object({                                     //  type: 'object', 
    x: Type.Number(),                               //  allOf: [{
  }),                                               //    type: 'object',
  Type.Object({                                     //    required: [ 'x' ],
    y: Type.Number(),                               //    properties: {
  })                                                //      x: { type: 'number' }
])                                                  //    }
                                                    //  }, {
                                                    //    type: 'object',
                                                    //    required: ['y'],
                                                    //    properties: {
                                                    //      y: { type: 'number' }
                                                    //    }
                                                    //  }]
                                                    // }

Never Schema Representation

Revision 0.26.0 simplifies the representation for TNever. Previous versions of TypeBox used an illogical intersection of Boolean constants via allOf. In 0.26.0, never is expressed as a not schema of type any.

Intersect 0.25.0

const T = Type.Never()                               // const T = {
                                                     //   allOf: [
                                                     //     { type: 'boolean', const: true }
                                                     //     { type: 'boolean', const: false }
                                                     //   ]
                                                     // }

Intersect 0.26.0

const T = Type.Never()                               // const T = { not: {} }

Value Cast and Convert

Revision 0.26.0 removes the Cast functions ability to coerce values. Use the new Convert function prior to Cast.

const T = Type.Number()

const V = Value.Cast(T, '42')                        // const V = 42   - 0.25.0 coerces to 42

const V = Value.Cast(T, Value.Convert(T, '42'))      //  const V = 42   - 0.26.0 convert then cast

Moved TypeGuard Module

The TypeGuard is now imported via the @sinclair/typebox module. This move is due to the TypeBox compositor internally using the guard when constructing types.

import { TypeGuard } from '@sinclair/typebox/guard' // 0.25.0

import { TypeGuard } from '@sinclair/typebox'       // 0.26.0

Format Renamed to FormatRegistry

The Format module has been renamed to FormatRegistry and moved to the typebox.ts module.

import { Format } from '@sinclair/typebox/format'    // 0.25.0

import { FormatRegistry } from '@sinclair/typebox'   // 0.26.0

Custom Renamed to TypeRegistry

The Format module has been renamed to FormatRegistry and moved to the typebox.ts module.

import { Custom } from '@sinclair/typebox/format' // 0.25.0

import { TypeRegistry } from '@sinclair/typebox'  // 0.26.0
typebox

Version Info

Tagged at
4 weeks ago