## [0.26.0](https://www.npmjs.com/package/@sinclair/typebox/v/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 - Enhancements - [Standard Type Builder](#Standard-Type-Builder) - [Automatic Unwrap for Union and Intersect](#Automatic-Unwrap-for-Union-and-Intersect) - [Intersect and Union now Compose](#Intersect-and-Union-now-Compose) - [Runtime Conditional Types](#Runtime-Conditional-Types) - [Value Convert](#Value-Convert) - [Error Iterator](#Error-Iterator) - [Codegen without JIT](#Codegen-without-JIT) - [Standard Type (Composite)](#Standard-Type-Composite) - [Standard Type (Not)](#Standard-Type-Not) - [Extended Type (BigInt)](#Extended-Type-BigInt) - [Extended Type (Symbol)](#Extended-Type-Symbol) - Breaking - [Minimum TypeScript Version](#Minimum-TypeScript-Version) - [Intersect Schema Representation](#Intersect-Schema-Representation) - [Never Schema Representation](#Never-Schema-Representation) - [Value Cast and Convert](#Value-Cast-and-Convert) - [Moved TypeGuard Module](#Moved-TypeGuard-Module) - [Format Renamed to FormatRegistry](#Format-Renamed-to-FormatRegistry) - [Custom Renamed to TypeRegistry](#Custom-Renamed-to-TypeRegistry) ## 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. ```typescript 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. ```typescript 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. ```typescript 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 ```typescript type T0 = string extends number ? true : false // ^ false type T1 = Extract // ^ number type T2 = Exclude // ^ string ``` #### TypeBox ```typescript const T0 = Type.Extends(Type.String(), Type.Number(), Type.Literal(true), Type.Literal(false)) // ^ TLiteral 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 ``` ## 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. ```typescript 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. ```typescript 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. ```typescript 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 ```typescript 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 ```typescript 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. ```typescript 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. ```typescript const T = Type.Symbol() Value.Check(A, Symbol('Foo')) // true ``` ## Extended Type (BigInt) Revision 0.26.0 provides provisional support for `BigInt` type validation. ```typescript 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 ```typescript 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 ```typescript 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 ```typescript const T = Type.Never() // const T = { // allOf: [ // { type: 'boolean', const: true } // { type: 'boolean', const: false } // ] // } ``` #### Intersect 0.26.0 ```typescript 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`. ```typescript 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. ```typescript 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. ```typescript 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. ```typescript import { Custom } from '@sinclair/typebox/format' // 0.25.0 import { TypeRegistry } from '@sinclair/typebox' // 0.26.0 ```