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.
- Enhancements
- Breaking
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
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
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;
// }
Revision 0.26.0 adds the runtime conditional types Extends
, Extract
and Exclude
as standard.
type T0 = string extends number ? true : false
// ^ false
type T1 = Extract<string | number, number>
// ^ number
type T2 = Exclude<string | number, number>
// ^ string
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>
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
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', ... }]
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)
// )
// }
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.
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
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
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
// }
Revision 0.26.0 provides provisional support for Symbol
type validation.
const T = Type.Symbol()
Value.Check(A, Symbol('Foo')) // true
Revision 0.26.0 provides provisional support for BigInt
type validation.
const T = Type.BigInt({ minimum: 10n })
Value.Check(B, 1_000_000n) // true
The following are breaking changed in Revision 0.26.0
Revision 0.26.0 requires a minimum recommended TypeScript version of 4.2.3
. Version 4.1.5
is no longer supported.
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.
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'
// }
// }
// }
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' }
// }
// }]
// }
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
.
const T = Type.Never() // const T = {
// allOf: [
// { type: 'boolean', const: true }
// { type: 'boolean', const: false }
// ]
// }
const T = Type.Never() // const T = { not: {} }
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
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
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
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