deno.land / x / typebox@0.32.21 / changelog / 0.32.0.md
Revision 0.32.0 adds support for ESM and carries out the work necessary to fully modularize the TypeBox type system to enable selective type imports. This revision also adds three new types (Mapped, Const, and Deref), along with two new Value functions (Clean and Default) as well as many enhancements to existing types (Index, KeyOf, RegExp, Optional and Readonly). This revision also carries out many internal optimizations to enhance type inference across all types.
This revision is a milestone revision for the TypeBox project. It has several breaking changes and requires a minor revision.
Revision 0.32.0 adds the ability to import types individually.
import { Type, type Static } from '@sinclair/typebox' // classic - 37.0 kb minified
import { Object, String, Number, type Static } from '@sinclair/typebox' // selective - 6.5 kb minified
Revision 0.32.0 adds the ability to import value functions from the /value
module path.
import { Value } from '@sinclair/typebox/value' // classic - 61.5 kb minified
import { Check } from '@sinclair/typebox/value' // selective - 18.2 kb minified
Revision 0.32.0 now publishes both CommonJS and ESM builds of TypeBox. Existing CommonJS users should not be impacted by the addition of ESM. For ESM users however, particularily those using bundlers, it's now possible to benefit from deep tree shake optimizations provided by modern bundler tooling.
Revision 0.32.0 adds three new types to the type system and makes enhancements to Readonly and Optional modifiers.
Revision 0.32.0 adds the Mapped type which replicates TS Mapped Types at runtime. The following shows the syntax comparison between TypeScript and TypeBox.
type T = {
x: number,
y: number,
z: number
}
type M = { [K in keyof T]: T[K] } // a mapped type
const T = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number()
})
const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) // a mapped type
Mapped types use a functional design to replicate the TypeScript feature. For users interested in this type, it may be helpful to use the TypeBox Workbench which can generate runtime Mapped types from TypeScript syntax.
Revision 0.32.0 adds a new Const type that creates readonly
types from object, array and primitive literal values. This type analogs the TypeScript as const
syntax. The following shows general usage.
const A = Type.Const(1 as const) // const A: TLiteral<1>
const B = Type.Const([1, 2, 3] as const) // const B: TReadonly<TTuple<[
// TLiteral<1>,
// TLiteral<2>,
// TLiteral<3>
// ]>>
const C = Type.Const({ // const C: TObject<{
x: 1, // x: TReadonly<TLiteral<1>>,
y: 2, // y: TReadonly<TLiteral<2>>,
z: 3 // z: TReadonly<TLiteral<3>>,
} as const) // }>
Revision 0.32.0 continues support for TypeScript 4.0, and because of this, the as const
syntax must be appended to each literal value passed to the Const type. When TypeBox ends support for 4.0, updates will be made to this type to make use of Const Type Parameters. This update will enable TypeBox to correctly infer the readonly literal type without the need for as const
.
Revision 0.32.0 adds a new Type.Deref type which can be used to dereference type schematics.
const Vector = Type.Object({ // const Vector = {
x: Type.Number(), // type: 'object',
y: Type.Number(), // required: ['x', 'y', 'z'],
}, { $id: 'Vector' }) // properties: {
// x: { type: 'number' },
// y: { type: 'number' }
// },
// $id: 'Vector'
// }
const VectorRef = Type.Ref(Vector) // const VectorRef = {
// $ref: 'Vector'
// }
// ... Embedded Reference Type
const Vertex = Type.Object({ // const Vertex = {
position: VectorRef, // type: 'object',
texcoord: VectorRef, // required: ['position', 'texcoord'],
}) // properties: {
// position: { $ref: 'Vector' },
// texcoord: { $ref: 'Vector' }
// }
// }
// ... Dereferenced Embedded Reference Type
const VertexDeref = Type.Deref(Vertex, [Vector]) // const VertexDeref = {
// type: 'object',
// required: ['position', 'texcoord'],
// properties: {
// position: {
// type: 'object',
// required: ['x', 'y', 'z'],
// properties: {
// x: { type: 'number' },
// y: { type: 'number' }
// }
// },
// texcoord: {
// type: 'object',
// required: ['x', 'y', 'z'],
// properties: {
// x: { type: 'number' },
// y: { type: 'number' }
// }
// }
// }
// }
The addition of Deref was prompted by issues composing reference types with mapping types (such as Partial, Required, Pick and Omit) which is generally not supported. Prior to Revision 0.32.0, there was some expectation for users to maintain and dereference types manually. In 0.32.0, users will still need to maintain references, but Deref will offer a more convenient mechanism to normalize reference types prior to composition.
Revision 0.32.0 updates RegExp to support the full ECMA 262 regular expression syntax. In previous revisions, this type had been expressed as an alias for TString
with a pattern
to try ensure compliance with the regular expression subset supported by Json Schema. In Revision 0.32.0, RegExp is given a new type representation unto itself (named TRegExp
) which houses both source
and flags
properties used to reconstruct a JavaScript regular expression object, making it properly distinct from TString
and fully supportive of UTF-16.
// Case Insensitive
const T = Type.RegExp(/abc/i) // const T = {
// type: 'RegExp',
// source: 'abc',
// flags: 'i'
// }
type T = Static<typeof T> // type T = string
Value.Check(T, 'abc') // ok
Value.Check(T, 'ABC') // ok
// Extended Syntax
const E = Type.RegExp(/<a?:.+?:\d{18}>|\p{Extended_Pictographic}/gu)
Value.Check(E, '♥️♦️♠️♣️') // ok - emoji supported
The RegExp type can be thought of as a more capable TemplateLiteral that can only reasonably infer as string
. Additionally, the RegExp inference type of string
is unique to the other [JavaScript]
types in that it does not infer as it's named type. The updates to RegExp were prompted by the limitations with Json Schema expressions, and to provide better options for users requiring general Unicode validation support. For Json Schema compliance, the recommendation moving forward will be to use either String with pattern or TemplateLiteral.
const T = Type.String({ pattern: '^(a|b|c)$' }) // Json Schema compliant
const T = Type.TemplateLiteral('${a|b|c}') // Json Schema compliant
const T = Type.RegExp(/$(a|b|c)$/) // Non Json Schema compliant
Revision 0.32.0 adds new overloads for Readonly and Optional modifiers that enable them to subtract (or remove) that modifier from a type. Both Readonly and Optional now accept an optional secondary boolean argument that if false
, will remove the modifier.
type T = {
x?: number,
y?: number
}
type M = { [K in keyof T]-?: T[K] } // -? - subtract optional modifier
const T = Type.Object({
x: Type.Optional(Type.Number()),
y: Type.Optional(Type.Number())
})
const M = Type.Mapped(Type.KeyOf(T), K => {
return Type.Optional(Type.Index(T, K), false) // false - subtract optional modifier
})
Subtractive modifiers are provided in support of the new Mapped type feature.
Revision 0.32.0 adds two new functions to the Value module.
Revision 0.32.0 adds a new Clean function that can be used to omit any values unknown to the type. This function will work irrespective of if additionalProperties
is specified on the type. The Clean function is intended to replicate the functionality of Ajv's removeAdditional
configuration.
const T = Type.Object({
x: Type.Number(),
y: Type.Number()
})
const X = Value.Clean(T, null) // const 'X = null
const Y = Value.Clean(T, { x: 1 }) // const 'Y = { x: 1 }
const Z = Value.Clean(T, { x: 1, y: 2, z: 3 }) // const 'Z = { x: 1, y: 2 }
Note: the Clean function does not check the validity of the value being cleaned, and does not provide assurances that the result will be valid. Its return value is unknown
and should be checked before use.
Revision 0.32.0 adds a new Default function that can be used to add missing values if the type specifies a default
annotation. This function is intended to replicate Ajv's useDefaults
functionality.
const T = Type.Object({
x: Type.Number({ default: 0 }),
y: Type.Number({ default: 0 })
})
const X = Value.Default(T, null) // const 'X = null - non-enumerable
const Y = Value.Default(T, { }) // const 'Y = { x: 0, y: 0 }
const Z = Value.Default(T, { x: 1 }) // const 'Z = { x: 1, y: 0 }
The Default function does not check the validity of the value being defaulted, and does not provide assurances that the result will be valid. Its return value is unknown
and should be checked before use.
Following the work to modularize TypeBox's type system, additional optimizations were carried out across each submodule to only import dependent type infrastructure. This has led to some fairly significant reductions in output sizes across each submodule. The main TypeBox import has increased in size due in part to the new Mapped types feature and other associative types, however selective imports supported on this revision should offer options for users concerned about output bundle size. There will be contined work to optimize the new type system throughout 0.32.0 and subsequent revisions.
The following shows the comparisons between 0.31.0 and 0.32.0.
// Revision 0.31.0
┌──────────────────────┬────────────┬────────────┬─────────────┐
│ (index) │ Compiled │ Minified │ Compression │
├──────────────────────┼────────────┼────────────┼─────────────┤
│ typebox/compiler │ '163.6 kb' │ ' 71.6 kb' │ '2.28 x' │
│ typebox/errors │ '113.3 kb' │ ' 50.1 kb' │ '2.26 x' │
│ typebox/system │ ' 83.9 kb' │ ' 37.5 kb' │ '2.24 x' │
│ typebox/value │ '191.1 kb' │ ' 82.3 kb' │ '2.32 x' │
│ typebox │ ' 73.8 kb' │ ' 32.3 kb' │ '2.29 x' │
└──────────────────────┴────────────┴────────────┴─────────────┘
// Revision 0.32.0
┌──────────────────────┬────────────┬────────────┬─────────────┐
│ (index) │ Compiled │ Minified │ Compression │
├──────────────────────┼────────────┼────────────┼─────────────┤
│ typebox/compiler │ '120.6 kb' │ ' 52.9 kb' │ '2.28 x' │
│ typebox/errors │ ' 55.7 kb' │ ' 25.5 kb' │ '2.19 x' │
│ typebox/system │ ' 4.7 kb' │ ' 2.0 kb' │ '2.33 x' │
│ typebox/value │ '146.2 kb' │ ' 62.0 kb' │ '2.36 x' │
│ typebox │ ' 91.4 kb' │ ' 37.8 kb' │ '2.42 x' │
└──────────────────────┴────────────┴────────────┴─────────────┘
Revision 0.32.0 makes some enhancements to errors.
Revision 0.32.0 updates TypeBox's ErrorFunction to accept an ErrorParameter that contains additional information regarding the cause of a validation error. In Revision 0.31.0, only errorType
and schema
were passed through to this function. In 0.32.0 the additional properties value
and path
are also passed through. This update was prompted by some users needing to be able to generate specific error messages derived from specific values or other associated information.
The following shows the changes from 0.31.0 to 0.32.0.
// Revision 0.31.0
import { TypeSystemErrorFunction } from '@sinclair/typebox/system'
TypeSystemErrorFunction.Set((schema, errorType) => {
return 'oh no, an error!'
})
// Revision 0.32.0
import { SetErrorFunction } from '@sinclair/typebox/errors'
SetErrorFunction(({ schema, errorType, path, value }) => { // as destructured object
return 'oh no, an error!'
})
Note that Revision 0.32.0 does make a breaking interface change by moving the ErrorFunction from /system
to /errors
. See breaking changes for more information.
The following list the breaking changes in Revision 0.32.0.
Revision 0.32.0 renames the Optional
, Required
and Transform
symbols to OptionalKind
, RequiredKind
and TransformKind
. This change was necessary to avoid conflicts with exported type functions.
// Revision 0.31.0
import { Kind, Hint, Optional, Required, Transform } from '@sinclair/typebox' // these are symbols
// Revision 0.32.0
import {
Kind, Hint, OptionalKind, RequiredKind, TransformKind, // these are symbols
Optional, Required, Transform // these are type imports
} from '@sinclair/typebox'
Revision 0.32.0 has a breaking interface change on the TypeGuard utility where the T
prefixed guard functions have been updated to use the Is
prefix. This naming change is perhaps somewhat more sensible than the previous naming, however the update was largely prompted by TypeScript compiler issues where interface types (i.e. TString
) where conflicting with the TString
functions leading to breakage in CommonJS.
// Revision 0.31.0
import { TypeGuard, Kind } from '@sinclair/typebox'
const R = TypeGuard.TString({ ... })
// Revision 0.32.0
import { TypeGuard } from '@sinclair/typebox'
const R = TypeGuard.IsString({ ... })
The value submodule function import paths are unfortunately no longer supported. Instead, these can be imported directly on the /value
path. The need to break the submodule paths was mostly due to complexities configuring dual ESM and CommonJS publishing for the package, as well as retaining support for pre and post node16 module resolution (of which many complexities reside, both for Node as well as for TypeScript type module resolution)
// Revision 0.31.0
import { Check } from '@sinclair/typebox/value/check'
// Revision 0.32.0
import { Check } from '@sinclair/typebox/value'
The TypeSystemErrorFunction has been replaced with SetErrorFunction which can be imported on the /errors
submodule. This change is generally a tidy up, and to reserve the /system
submodule for type system policy configuration, as well as future Json Schema generation options (draft 2020-12)
// Revision 0.31.0
import { TypeSystemErrorFunction, ValueErrorType, DefaultErrorFunction } from '@sinclair/typebox/system'
TypeSystemErrorFunction.Set((schema, errorType) => { // i18n override
switch(errorType) {
/* en-US */ case ValueErrorType.String: return 'Expected string'
/* fr-FR */ case ValueErrorType.Number: return 'Nombre attendu'
/* ko-KR */ case ValueErrorType.Boolean: return '예상 부울'
/* en-US */ default: return DefaultErrorFunction(schema, errorType)
}
})
// Revision 0.32.0
import { SetErrorFunction, ValueErrorType, DefaultErrorFunction } from '@sinclair/typebox/errors'
SetErrorFunction((error) => { // i18n override
switch(error.errorType) {
/* en-US */ case ValueErrorType.String: return 'Expected string'
/* fr-FR */ case ValueErrorType.Number: return 'Nombre attendu'
/* ko-KR */ case ValueErrorType.Boolean: return '예상 부울'
/* en-US */ default: return DefaultErrorFunction(error)
}
})
This RegEx function was flagged for deprecation on 0.30.0. It has been removed on Revision 0.32.0. Use the Type.RegExp type, or Type.String with a pattern to remain compatible with the Json Schema specification.
// Revision 0.31.0
const T = Type.RegEx(/abc/) // deprecation warning
// Revision 0.32.0
const A = Type.RegExp(/abc/) // JavaScript Type
const B = Type.String({ pattern: /abc/.source }) // Json Type
Version Info