Publish
This commit is contained in:
15
changelog/0.17.1.md
Normal file
15
changelog/0.17.1.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## [0.17.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.1)
|
||||
|
||||
- Remove default `additionalProperties: false` constraint from all object schemas.
|
||||
|
||||
This update removes the `additionalProperties: false` constraint on all object schemas. This constraint was introduced on `0.16.x` but has resulted in significant downstream problems composing schemas whose types `intersect`. This is due to a JSON schema design principle where constraints should only be added (never removed), and that intersection types may require removal of the `additionalProperties` constraint in some cases, this had resulted in some ambiguity with respect to how TypeBox should handle such intersections.
|
||||
|
||||
This update can also be seen as a precursor towards TypeBox potentially leveraging `unevaluatedProperties` for type intersection in future releases. Implementers should take note that in order to constrain the schema to known properties, one should apply the `additionalProperties: false` as the second argument to `Type.Object({...})`.
|
||||
|
||||
```typescript
|
||||
const T = Type.Object({
|
||||
a: Type.String(),
|
||||
b: Type.Number()
|
||||
}, {
|
||||
additionalProperties: false
|
||||
})
|
||||
38
changelog/0.17.4.md
Normal file
38
changelog/0.17.4.md
Normal file
@@ -0,0 +1,38 @@
|
||||
## [0.17.4](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.4)
|
||||
|
||||
Changes:
|
||||
|
||||
- Added `Type.Box()` and `Type.Ref()` functions.
|
||||
|
||||
Notes:
|
||||
|
||||
This update provides the `Type.Box()` function to enable common related schemas to grouped under a common namespace; typically expressed as a `URI`. This functionality is primarily geared towards allowing one to define a common set of domain objects that may be shared across application domains running over a network. The `Type.Box()` is intended to be an analog to `XML` `xmlns` namespaces.
|
||||
|
||||
The `Type.Ref()` function is limited to referencing from boxes only. The following is an example.
|
||||
|
||||
```typescript
|
||||
// Domain objects for the fruit service.
|
||||
const Fruit = Type.Box('https://fruit.domain.com', {
|
||||
Apple: Type.Object({ ... }),
|
||||
Orange: Type.Object({ ... }),
|
||||
})
|
||||
|
||||
// An order referencing types of the fruit service.
|
||||
const Order = Type.Object({
|
||||
id: Type.String(),
|
||||
quantity: Type.Number(),
|
||||
item: Type.Union([
|
||||
Type.Ref(Fruit, 'Apple'),
|
||||
Type.Ref(Fruit, 'Orange')
|
||||
])
|
||||
})
|
||||
```
|
||||
> Note: As of this release, the `Type.Omit()`, `Type.Pick()`, `Type.Partial()`, `Type.Readonly()` and `Type.Intersect()` functions do not work with Reference Types. This may change in later revisions.
|
||||
|
||||
For validation using `Ajv`, its possible to apply the `Box` directly as a schema.
|
||||
|
||||
```typescript
|
||||
ajv.addSchema(Fruit) // makes all boxed types known to Ajv
|
||||
```
|
||||
|
||||
This functionality is flagged as `EXPERIMENTAL` and awaits community feedback.
|
||||
56
changelog/0.17.6.md
Normal file
56
changelog/0.17.6.md
Normal file
@@ -0,0 +1,56 @@
|
||||
## [0.17.6](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.6)
|
||||
|
||||
Changes:
|
||||
|
||||
- Added `Type.Rec(...)` function.
|
||||
|
||||
Notes:
|
||||
|
||||
This update introduces the `Type.Rec()` function for enabling Recursive Types. Please note that due to current inference limitations in TypeScript, TypeBox is unable to infer the type and resolves inner types to `any`.
|
||||
|
||||
This functionality enables for complex self referential schemas to be composed. The following creates a binary expression syntax node with the expression self referential for left and right operands.
|
||||
|
||||
```typescript
|
||||
const Operator = Type.Union([
|
||||
Type.Literal('+'),
|
||||
Type.Literal('-'),
|
||||
Type.Literal('/'),
|
||||
Type.Literal('*')
|
||||
])
|
||||
|
||||
type Expression = Static<typeof Expression>
|
||||
|
||||
// Defines a self referencing type.
|
||||
const Expression = Type.Rec(Self => Type.Object({
|
||||
left: Type.Union([Self, Type.Number()]),
|
||||
right: Type.Union([Self, Type.Number()]),
|
||||
operator: Operator
|
||||
}))
|
||||
|
||||
function evaluate(expression: Expression): number {
|
||||
const left = typeof expression.left !== 'number'
|
||||
? evaluate(expression.left as Expression) // assert as Expression
|
||||
: expression.left
|
||||
const right = typeof expression.right !== 'number'
|
||||
? evaluate(expression.right as Expression) // assert as Expression
|
||||
: expression.right
|
||||
switch(expression.operator) {
|
||||
case '+': return left + right
|
||||
case '-': return left - right
|
||||
case '*': return left * right
|
||||
case '/': return left / right
|
||||
}
|
||||
}
|
||||
|
||||
const result = evaluate({
|
||||
left: {
|
||||
left: 10,
|
||||
operator: '*',
|
||||
right: 4,
|
||||
},
|
||||
operator: '+',
|
||||
right: 2,
|
||||
}) // -> 42
|
||||
```
|
||||
|
||||
This functionality is flagged as `EXPERIMENTAL` and awaits community feedback.
|
||||
22
changelog/0.18.0.md
Normal file
22
changelog/0.18.0.md
Normal file
@@ -0,0 +1,22 @@
|
||||
## [0.18.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.18.0)
|
||||
|
||||
Changes:
|
||||
|
||||
- Function `Type.Intersect(...)` is now implemented with `allOf` and constrained with `unevaluatedProperties` (draft `2019-09`)
|
||||
- Function `Type.Dict(...)` has been deprecated and replaced with `Type.Record(...)`.
|
||||
- Function `Type.Strict(...)` now includes the `$schema` property referencing the `2019-09` draft.
|
||||
|
||||
### Type.Intersect(...)
|
||||
|
||||
TypeBox now targets JSON schema draft `2019-09` for expressing `Type.Intersect(...)`. This is now expressed via `allOf` with additionalProperties constrained with `unevaluatedProperties`. Note that `unevaluatedProperties` is a feature of the `2019-09` specification.
|
||||
|
||||
### Type.Record(K, V)
|
||||
|
||||
TypeBox has deprecated `Type.Dict(...)` in favor of the more generic `Type.Record(...)`. Where as `Type.Dict(...)` was previously expressed with `additionalProperties: { ... }`, `Type.Record(...)` is expressed with `patternProperties` and supports both `string` and `number` indexer keys. Additionally, `Type.Record(...)` supports string union arguments. This is analogous to TypeScript's utility record type `Record<'a' | 'b' | 'c', T>`.
|
||||
|
||||
## [0.17.7](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.7)
|
||||
|
||||
Changes:
|
||||
|
||||
- Added optional `$id` argument on `Type.Rec()`.
|
||||
- Documentation updates.
|
||||
5
changelog/0.18.1.md
Normal file
5
changelog/0.18.1.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.18.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.18.1)
|
||||
|
||||
- Function `Type.Enum(...)` now expressed with `anyOf`. This to remove the `allowUnionTypes` configuration required to use `enum` with in AJV strict.
|
||||
- Function `Type.Rec(...)` now takes a required `$id` as the first parameter.
|
||||
- Function `Type.Strict(...)` no longer includes a `$schema`. Callers can now optionally pass `CustomOptions` on `Type.Strict(...)`
|
||||
20
changelog/0.19.0.md
Normal file
20
changelog/0.19.0.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## [0.19.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.19.0)
|
||||
|
||||
Updates:
|
||||
|
||||
- Function `Type.Box(...)` removes `$id` parameter as first argument.
|
||||
- Function `Type.Ref(...)` is now overloaded to support referencing `Type.Box(...)` and `TSchema`.
|
||||
|
||||
Notes:
|
||||
|
||||
This update changes the signature of `Type.Box(...)` and removes the explicit `$id` passing on the first parameter. The `$id` must be passed as an option if the caller wants to reference that type.
|
||||
|
||||
```typescript
|
||||
const T = Type.String({ $id: 'T' })
|
||||
|
||||
const B = Type.Box({ T }, { $id: 'B' })
|
||||
|
||||
const R1 = Type.Ref(T) // const R1 = { $ref: 'T' }
|
||||
|
||||
const R2 = Type.Ref(B, 'T') // const R2 = { $ref: 'B#/definitions/T' }
|
||||
```
|
||||
17
changelog/0.20.0.md
Normal file
17
changelog/0.20.0.md
Normal file
@@ -0,0 +1,17 @@
|
||||
## [0.20.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.20.0)
|
||||
|
||||
Updates:
|
||||
|
||||
- Function `Type.Rec(...)` signature change.
|
||||
- Minor documentation updates.
|
||||
|
||||
Notes:
|
||||
|
||||
The `Type.Rec(...)` function signature has been changed to allow passing the `$id` as a custom option. This is to align `Type.Rec(...)` with other functions that accept `$id` as an option. `Type.Rec(...)` can work with or without an explicit `$id`, but it is recommend to specify one if the recursive type is nested in an outer schema.
|
||||
|
||||
```typescript
|
||||
const Node = Type.Rec(Self => Type.Object({
|
||||
id: Type.String(),
|
||||
nodes: Type.Array(Self)
|
||||
}), { $id: 'Node' })
|
||||
```
|
||||
5
changelog/0.20.1.md
Normal file
5
changelog/0.20.1.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.20.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.20.1)
|
||||
|
||||
Updates:
|
||||
|
||||
- TypeBox mandates TypeScript compiler version `4.3.5` and above.
|
||||
7
changelog/0.21.0.md
Normal file
7
changelog/0.21.0.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## [0.21.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.21.0)
|
||||
|
||||
Updates:
|
||||
|
||||
- TypeBox static inference has been updated inline with additional inference constraints added in TypeScript 4.5. All types now include a phantom `_infer` property which contains the inference TS type for a given schema. The type of this property is inferred at the construction of the schema, and referenced directly via `Static<T>`.
|
||||
- `Type.Box(...)` has been renamed to `Type.Namespace(...)` to draw an analogy with XML's `xmlns` XSD types.
|
||||
|
||||
26
changelog/0.21.2.md
Normal file
26
changelog/0.21.2.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## [0.21.2](https://www.npmjs.com/package/@sinclair/typebox/v/0.21.2)
|
||||
|
||||
Updates:
|
||||
|
||||
- TypeBox now correctly infers for nested union and intersect types.
|
||||
|
||||
Before
|
||||
|
||||
```typescript
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.String() })
|
||||
const C = Type.Object({ c: Type.String() })
|
||||
const T = Type.Intersect([A, Type.Union([B, C])])
|
||||
|
||||
// type T = { a: string } & { b: string } & { c: string }
|
||||
```
|
||||
After
|
||||
|
||||
```typescript
|
||||
const A = Type.Object({ a: Type.String() })
|
||||
const B = Type.Object({ b: Type.String() })
|
||||
const C = Type.Object({ c: Type.String() })
|
||||
const T = Type.Intersect([A, Type.Union([B, C])])
|
||||
|
||||
// type T = { a: string } & ({ b: string } | { c: string })
|
||||
```
|
||||
8
changelog/0.22.0.md
Normal file
8
changelog/0.22.0.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## [0.22.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.22.0)
|
||||
|
||||
Updates:
|
||||
|
||||
- The type `TSchema` is now expressed as an HKT compatible interface. All types now extend the `TSchema` interface and are themselves also expressed as interfaces. This work was undertaken to explore recursive type aliases in future releases.
|
||||
- The phantom property `_infer` has been renamed to `$static`. Callers should not interact with this property as it will always be `undefined` and used exclusively for optimizing type inference in TypeScript 4.5 and above.
|
||||
- TypeBox re-adds the feature to deeply introspect schema properties. This feature was temporarily removed on the `0.21.0` update to resolve deep instantiation errors on TypeScript 4.5.
|
||||
- The `Type.Box(...)` and `Type.Rec(...)` functions internally rename the property `definitions` to `$defs` inline with JSON schema draft 2019-09 conventions. Reference [here](https://opis.io/json-schema/2.x/definitions.html).
|
||||
8
changelog/0.23.0.md
Normal file
8
changelog/0.23.0.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## [0.23.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.0)
|
||||
|
||||
Updates:
|
||||
|
||||
- The types `Type.Namespace(...)` and `Type.Ref(...)` are promoted to `Standard`.
|
||||
- TypeBox now includes an additional type named `TRef<...>` that is returned on calls to `Type.Ref(...)`. The `TRef<...>` includes a new `RefKind` symbol for introspection of the reference type.
|
||||
- TypeBox now maintains an internal dictionary of all schemas passed that contain an `$id` property. This dictionary is checked whenever a user attempts to reference a type and will throw if attempting to reference a target schema with no `$id`.
|
||||
- The types `Type.Partial(...)`, `Type.Required(...)`, `Type.Omit()` and `Type.Pick(...)` now support reference types. Note that when using these functions with references, TypeBox will replicate the source schema and apply the necessary modifiers to the replication.
|
||||
5
changelog/0.23.1.md
Normal file
5
changelog/0.23.1.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.23.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.1)
|
||||
|
||||
Updates:
|
||||
|
||||
- The `Type.KeyOf(...)` type can now accept references of `Type.Ref(TObject)`
|
||||
5
changelog/0.23.3.md
Normal file
5
changelog/0.23.3.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.23.3](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.3)
|
||||
|
||||
Updates:
|
||||
|
||||
- Fix: Rename BoxKind to NamespaceKind
|
||||
28
changelog/0.24.0.md
Normal file
28
changelog/0.24.0.md
Normal file
@@ -0,0 +1,28 @@
|
||||
## [0.24.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.0)
|
||||
|
||||
Changes:
|
||||
|
||||
- The `kind` and `modifier` keywords are now expressed as symbol keys. This change allows AJV to leverage TypeBox schemas directly without explicit configuration of `kind` and `modifier` in strict mode.
|
||||
- `Type.Intersect([...])` now returns a composite `TObject` instead of a `allOf` schema representation. This change allows intersected types to be leveraged in calls to `Omit`, `Pick`, `Partial`, `Required`.
|
||||
- `Type.Void(...)` now generates a `{ type: null }` schema representation. This is principally used for RPC implementations where a RPC target function needs to respond with a serializable value for `void` return.
|
||||
- `Type.Rec(...)` renamed to `Type.Recursive(...)` and now supports non-mutual recursive type inference.
|
||||
|
||||
Added:
|
||||
|
||||
- `Type.Unsafe<T>(...)`. This type enables custom schema representations whose static type is informed by generic type T.
|
||||
- `Type.Uint8Array(...)`. This is a non-standard schema that can be configured on AJV to enable binary buffer range validation.
|
||||
- Added optional extended `design` property on all schema options. This property can be used to specify design time metadata when rendering forms.
|
||||
|
||||
Compiler:
|
||||
|
||||
- TypeBox now provides an optional experimental type compiler that can be used to validate types without AJV. This compiler is not a standard JSON schema compiler and will only compile TypeBox's known schema representations. For full JSON schema validation, AJV should still be the preference. This compiler is a work in progress.
|
||||
|
||||
Value:
|
||||
|
||||
- TypeBox now provides a value generator that can generate default values from TypeBox types.
|
||||
|
||||
Breaking Changes:
|
||||
|
||||
- `Type.Intersect(...)` is constrained to accept types of `TObject` only.
|
||||
- `Type.Namespace(...)` has been removed.
|
||||
- The types `TUnion`, `TEnum`, `KeyOf` and `TLiteral<TString>[]` are all now expressed via `allOf`. For Open API users, Please consider `Type.Unsafe()` to express `enum` string union representations. Documentation on using `Type.Unsafe()` can be found [here](https://github.com/sinclairzx81/typebox#Unsafe-Types)
|
||||
10
changelog/0.24.15.md
Normal file
10
changelog/0.24.15.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## [0.24.15](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.15)
|
||||
|
||||
Added:
|
||||
- `Conditional.Extends(...)` This enables TypeBox to conditionally map types inline with TypeScripts structural equivalence checks. Tested against TypeScript 4.7.4.
|
||||
- `Conditional.Extract(...)` Which analogs TypeScripts `Extract<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union)
|
||||
- `Conditional.Exclude(...)` Which analogs TypeScripts `Exclude<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers)
|
||||
- `Type.Parameters(...)` Returns the parameters of a `TFunction` as a `TTuple`
|
||||
- `Type.ReturnType(...)` Returns the return type schema of a `TFunction`
|
||||
- `Type.ConstructorParameters(...)` Returns the parameters of a `TConstructor` as a `TTuple`
|
||||
- `Type.InstanceType(...)` Returns the instance type schema of a `TConstructor`
|
||||
18
changelog/0.24.44.md
Normal file
18
changelog/0.24.44.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## [0.24.44](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.44)
|
||||
|
||||
Updates:
|
||||
- [189](https://github.com/sinclairzx81/typebox/pull/189) Both `Value.Error(T, value)` and `TypeCheck.Error(value)` now return an iterator for validation errors.
|
||||
- [191](https://github.com/sinclairzx81/typebox/pull/191) TypeBox now provides a `TypeGuard` API that can be used to check the structural validity of TypeBox type. The TypeGuard can be used in reflection / code generation scenarios to resolve the appropriate inner `TSchema` type while traversing a outer type.
|
||||
- [197](https://github.com/sinclairzx81/typebox/pull/197) TypeBox now implements conditional runtime type mapping. This functionality is offered as separate import for the `0.24.0` release but may be provided as standard type in later releases. This API enables `type T = Foo extends Bar ? true : false` conditional checks to be implemented at runtime. This API also provides the `Exclude` and `Extract` utility types which are implemented through conditional types in TypeScript.
|
||||
- [199](https://github.com/sinclairzx81/typebox/pull/199) TypeBox now provides better support for variadic function and constructor signatures. Currently variadic types are mapped as `Tuple` types.
|
||||
- [200](https://github.com/sinclairzx81/typebox/pull/200) The types `TPick` and `TOmit` now support types of `TUnion<TLiteral<string>[]>` to be used to select properties. Additionally, `KeyOf` now returns `TUnion<TLiteral<string>[]>`, allowing `KeyOf` schemas to be passed to `TPick` and `TOmit`.
|
||||
- [214](https://github.com/sinclairzx81/typebox/pull/214) TypeBox now provides better support for i18n. To achieve this, TypeBox includes fixed mappable error codes on the `ValueError` type. These codes can be used by external implementers to create localized error messages. TypeBox may include localized error codes as an optional import in future releases.
|
||||
- [288](https://github.com/sinclairzx81/typebox/pull/228) TypeBox now allows users to implement custom string validator formats. These formats are internally shared between the `Value` and `TypeCompiler` API's. TypeBox does not currently provide any built in formats, however the standard expected set (email, uuid, uri, etc) may be provided via optional import (inline with ajv-formats usage)
|
||||
- [229](https://github.com/sinclairzx81/typebox/pull/229) The `Value.Cast()` function now implements automatic coercion of string, number and Boolean types.
|
||||
- [231](https://github.com/sinclairzx81/typebox/pull/231) TypeBox provides a new `Value.Diff<T>()` and `Value.Patch<T>()` utility API for JavaScript values. This API is intended to provide a basis for the efficient transmission of state updates across a network. This API can diff any JavaScript value (typed or untyped) but is recommended to be used in conjunction with a formal static type.
|
||||
- [236](https://github.com/sinclairzx81/typebox/pull/236) TypeBox now implements the `TNever` type. This type is analogous to TypeScript's `never` type and is used in instances a composition results in a non-reconcilable type. Currently this type is implemented for empty `TUnion<[]>` types only. Future releases may utilize this type for planned updates to `TIntersect` (for example `string & number` resolves to `never`)
|
||||
- [241](https://github.com/sinclairzx81/typebox/pull/241) [247](https://github.com/sinclairzx81/typebox/pull/247) TypeBox now exposes a ValuePointer API that can be used to mutate a value via an RFC6901 JSON Pointer. Previously this functionality was internally used by `Value.Diff()` and `Value.Patch()` functions but is now offered as an optional import for implementations that need to update values manually through pointer references.
|
||||
|
||||
Additional:
|
||||
|
||||
- This project now includes two reference code generation utilities that can be used in custom build tooling. The first is `TypeScriptCodeGen` which will remap TypeScript `interface` and `type` definitions to TypeBox types. The second is `TypeBoxCodeGen` which will map existing TypeBox types into TypeScript type definitions. These implementations are not expected to be part of the TypeBox package, but users are free to clone and enhance them in their existing tool chains. Reference implementations can be found https://github.com/sinclairzx81/typebox/tree/master/codegen
|
||||
9
changelog/0.24.49.md
Normal file
9
changelog/0.24.49.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## [0.24.49](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.49)
|
||||
|
||||
Updates:
|
||||
|
||||
- [264](https://github.com/sinclairzx81/typebox/pull/264) TypeBox now provides preliminary support for non Boolean `additionalProperties`. This allows existing `TObject` schemas to be augmented with additional properties of a known type.
|
||||
|
||||
Additional:
|
||||
|
||||
- TypeBox provides an additional reference `codegen` module for generating raw JSON Schema from TypeScript types via the TS compiler API. This generator may be used in future tooling.
|
||||
20
changelog/0.24.6.md
Normal file
20
changelog/0.24.6.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## [0.24.6](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.6)
|
||||
|
||||
Added:
|
||||
|
||||
- TypeBox now offers a `TypeGuard` module for structurally checking TypeBox schematics. This module can be used in runtime type reflection scenarios where it's helpful to test a schema is of a particular form. This module can be imported under the `@sinclair/typebox/guard` import path.
|
||||
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
import { TypeGuard } from '@sinclair/typebox/guard'
|
||||
|
||||
const T: any = {} // T is any
|
||||
|
||||
const { type } = T // unsafe: type is any
|
||||
|
||||
if(TypeGuard.IsString(T)) {
|
||||
|
||||
const { type } = T // safe: type is 'string'
|
||||
}
|
||||
```
|
||||
6
changelog/0.24.8.md
Normal file
6
changelog/0.24.8.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## [0.24.8](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.8)
|
||||
|
||||
Added:
|
||||
- `Value.Cast(T, value)` structurally casts a value into another form while retaining information within the original value.
|
||||
- `Value.Check(T, value)` provides slow dynamic type checking for values. For performance, one should consider the `TypeCompiler` or `Ajv` validator.
|
||||
- `Value.Errors(T, value)` returns an iterator of errors found in a given value.
|
||||
7
changelog/0.25.0.md
Normal file
7
changelog/0.25.0.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## [0.25.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.0)
|
||||
|
||||
Updates:
|
||||
|
||||
- [271](https://github.com/sinclairzx81/typebox/pull/271) Adds a new non-standard `Type.Date()` type. This type joins the existing `Type.UInt8Array()` as a promoted extended type used to represent core JavaScript primitives. It's inclusion was prompted by end user requirements to validate Date objects prior to writing them to Date supported API's and where serialization of the Date object is handled internally by the API.
|
||||
|
||||
- [271](https://github.com/sinclairzx81/typebox/pull/271) Redesign of Extended Type representations. Extended types been updated to provide external validators (such as Ajv) additional standard proporties to use when defining the custom schema. These properties are `instanceOf` (used for validating a class `object` instances), and `typeOf` (when validating `value` types). Information on configuring AJV for these properties can be found in the AJV section of the TypeBox readme.
|
||||
5
changelog/0.25.10.md
Normal file
5
changelog/0.25.10.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.25.10](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.10)
|
||||
|
||||
Updates:
|
||||
|
||||
- [283](https://github.com/sinclairzx81/typebox/pull/283) Updates the custom type validator callback signature to accept a schema instance. The schema instance may include additional constraints (such as options) that may be used during the validation process. `Custom.Set('<Kind>', (schema, value) => { ... })`.
|
||||
5
changelog/0.25.11.md
Normal file
5
changelog/0.25.11.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.25.11](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.11)
|
||||
|
||||
Updates:
|
||||
|
||||
- [286](https://github.com/sinclairzx81/typebox/pull/286) implements a FNV1A-64 non cryptographic hashing function in TypeBox. This function should not be used in place of cryptographic hashing functions, rather it's purpose is to provide relatively fast, hashing mechanism to assist with checks for arrays with uniqueItems constraints, specifically for cases where the array may contains reference values (such as objects, arrays, Dates and Uint8Array). This function is provided via `Value.Hash()` for convenience as the hash may be useful to generate a numeric identifier for values (with some considerations to React array rendering in absence of key or identifier)
|
||||
5
changelog/0.25.18.md
Normal file
5
changelog/0.25.18.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.25.18](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.18)
|
||||
|
||||
Updates:
|
||||
|
||||
- [307](https://github.com/sinclairzx81/typebox/pull/307) implements date conversion when casting values with `Value.Cast(Type.Date(), ...)`. Castable values include numbers (interpretted as timestamps) and iso8601 string values. UNCASTABLE values will result in dates with values of `1970-01-01T00:00:00.000Z`. This version also includes more robust checks for Dates initialized with invalid values.
|
||||
5
changelog/0.25.22.md
Normal file
5
changelog/0.25.22.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.25.22](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.22)
|
||||
|
||||
Updates:
|
||||
|
||||
- [323](https://github.com/sinclairzx81/typebox/pull/323) adds compiler support for UTF-16 (unicode) characters for schema identifiers.
|
||||
10
changelog/0.25.23.md
Normal file
10
changelog/0.25.23.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## [0.25.23](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23)
|
||||
|
||||
Updates:
|
||||
|
||||
- [324](https://github.com/sinclairzx81/typebox/pull/324) TypeScript Language Service now presents JSDoc comments when inferring static object properties. (IntelliSense)
|
||||
- [325](https://github.com/sinclairzx81/typebox/pull/325) Additional property inference optimizations.
|
||||
|
||||
Additional:
|
||||
|
||||
- Huge thank you to GITHUB user [stevezhu](https://github.com/stevezhu) for these excellent contributions.
|
||||
10
changelog/0.25.24.md
Normal file
10
changelog/0.25.24.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## [0.25.24](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23)
|
||||
|
||||
Updates:
|
||||
|
||||
- [331](https://github.com/sinclairzx81/typebox/pull/331) Implements an additional check specific to property types of `required & undefined`. This to ensure the property key exists when the property value extends `undefined`.
|
||||
- [331](https://github.com/sinclairzx81/typebox/pull/331) Documentation updates for AJV and TypeCompiler
|
||||
|
||||
Additional:
|
||||
|
||||
- [331](https://github.com/sinclairzx81/typebox/pull/331) Remove unused recursive code paths for create and cast.
|
||||
5
changelog/0.25.9.md
Normal file
5
changelog/0.25.9.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.25.9](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.9)
|
||||
|
||||
Updates:
|
||||
|
||||
- [282](https://github.com/sinclairzx81/typebox/pull/282) TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('<Kind>', (value) => { ... })` which allow the TypeCompiler and Value API's to make use of user defined validation logic.
|
||||
394
changelog/0.26.0.md
Normal file
394
changelog/0.26.0.md
Normal file
@@ -0,0 +1,394 @@
|
||||
## [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)
|
||||
|
||||
<a name="Standard-Type-Builder"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Automatic-Unwrap-for-Union-and-Intersect"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Intersect-and-Union-now-Compose"></a>
|
||||
|
||||
## 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;
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Runtime-Conditional-Types"></a>
|
||||
|
||||
## 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<string | number, number>
|
||||
// ^ number
|
||||
type T2 = Exclude<string | number, number>
|
||||
// ^ string
|
||||
```
|
||||
|
||||
#### TypeBox
|
||||
```typescript
|
||||
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>
|
||||
```
|
||||
|
||||
<a name="Value-Convert"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Error-Iterator"></a>
|
||||
|
||||
## 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', ... }]
|
||||
```
|
||||
|
||||
|
||||
<a name="Codegen-without-JIT"></a>
|
||||
|
||||
## 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)
|
||||
// )
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Standard-Type-Not"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Extended-Type-Composite"></a>
|
||||
|
||||
## 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
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Extended-Type-Symbol"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Extended-Type-BigInt"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Breaking"></a>
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
The following are breaking changed in Revision 0.26.0
|
||||
|
||||
<a name="Minimum-TypeScript-Version"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="Intersect-Schema-Representation"></a>
|
||||
|
||||
## 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' }
|
||||
// }
|
||||
// }]
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Never-Schema-Representation"></a>
|
||||
|
||||
## 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: {} }
|
||||
```
|
||||
|
||||
<a name="Value-Cast-and-Convert"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Movied TypeGuard Module"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Format-Renamed-to-FormatRegistry"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="Custom-Renamed-to-TypeRegistry"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
38
changelog/0.26.2.md
Normal file
38
changelog/0.26.2.md
Normal file
@@ -0,0 +1,38 @@
|
||||
## [0.26.2](https://www.npmjs.com/package/@sinclair/typebox/v/0.26.2)
|
||||
|
||||
Updates:
|
||||
|
||||
- [331](https://github.com/sinclairzx81/typebox/pull/349) Revert 0.25.0 Intersect logic for Composite
|
||||
|
||||
Notes:
|
||||
|
||||
This PR reverts the logic on Type.Composite back to 0.25.0 Type.Intersect due to excessive type instantiation issues. On 0.26.0, Type.Composite attempted to take a union for overlapping properties, however due to the sophistication required to type map the unions for overlapping properties, this has resulted in type instantiation problems for some users upgrading to 0.26.0.
|
||||
|
||||
As such, 0.26.2 reverts back to the 0.25.0 interpretation, but applies type mappings more inline with TS's interpretation of an overlapping varying property types. In the examples below, the type `C` is the evaluated type for Type.Composite. Note that TS will not union for overlapping properties and instead evaluate `never`. The 0.26.2 implementation falls inline with this evaluation.
|
||||
|
||||
```typescript
|
||||
{ // evaluation case 1: non-varying
|
||||
type T = { a: number } & { a: number }
|
||||
|
||||
type C = {[K in keyof T]: T[K] } // type C = { a: number }
|
||||
}
|
||||
|
||||
{ // evaluation case 2: varying
|
||||
type T = { a: number } & { a: string }
|
||||
|
||||
type C = {[K in keyof T]: T[K] } // type C = { a: never }
|
||||
}
|
||||
|
||||
{ // evaluation case 3: single optional
|
||||
type T = { a?: number } & { a: number }
|
||||
|
||||
type C = {[K in keyof T]: T[K] } // type C = { a: number }
|
||||
}
|
||||
|
||||
{ // evaluation case 4: all optional
|
||||
type T = { a?: number } & { a?: number }
|
||||
|
||||
type C = {[K in keyof T]: T[K] } // type C = { a?: number | undefined }
|
||||
}
|
||||
```
|
||||
Note: the Type.Composite is intended to be a temporary type which can be replaced with a more general `Type.Mapped` in future revisions of TypeBox. As the infrastructure to support mapped types does not exist, users can use Type.Composite to partially replicate mapped type evaluation for composited object types only.
|
||||
152
changelog/0.27.0.md
Normal file
152
changelog/0.27.0.md
Normal file
@@ -0,0 +1,152 @@
|
||||
## [0.27.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.27.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.27.0 adds support for runtime [Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html). This revision does not include any functional breaking changes but does rename some public type aliases. As such this revision requires a minor semver increment.
|
||||
|
||||
## Contents
|
||||
|
||||
- Enhancements
|
||||
- [Template Literal Types](#Template-Literal-Types)
|
||||
- [TemplateLiteralParser](#TemplateLiteralParser)
|
||||
- [WorkBench](#WorkBench)
|
||||
- Breaking Changes
|
||||
- [TSelf renamed to TThis](#TSelf-renamed-to-TThis)
|
||||
|
||||
|
||||
<a href="Template-Literal-Types"></a>
|
||||
|
||||
## Template Literal Types
|
||||
|
||||
Revision 0.27.0 adds support for Template Literal types. These types operate as a form of computed `TUnion<TLiteral<string>>`. TypeBox encodes template literals using a subset of [ECMA 262](https://json-schema.org/understanding-json-schema/reference/regular_expressions.html) regular erxpressions which are applied to `pattern` properties of type `string`. This encoding enables JSON Schema validators to assert using existing regular expression checks.
|
||||
|
||||
### TypeScript
|
||||
|
||||
TypeScript defines Template Literals using back tick quoted strings which may include embedded union groups.
|
||||
|
||||
```typescript
|
||||
type T = `option${'A'|'B'}` // type T = 'optionA' | 'optionB'
|
||||
|
||||
type R = Record<T, string> // type R = {
|
||||
// optionA: string
|
||||
// optionB: string
|
||||
// }
|
||||
```
|
||||
|
||||
### TypeBox
|
||||
|
||||
TypeBox defines Template Literals using the `TemplateLiteral` function. This function accepts a sequence of TLiteral, TString, TNumber, TInteger and TBigInt which describe a sequence concatenations. The embedded TUnion type defines an option group which can later be expanded into a set of `TLiteral<string>`. This expansion enables Template Literal types to also be used as Record keys.
|
||||
|
||||
```typescript
|
||||
const T = Type.TemplateLiteral([ // const T = {
|
||||
Type.Literal('option'), // pattern: '^option(A|B)$',
|
||||
Type.Union([ // type: 'string'
|
||||
Type.Literal('A'), // }
|
||||
Type.Literal('B')
|
||||
])
|
||||
])
|
||||
|
||||
const R = Type.Record(T, Type.String()) // const R = {
|
||||
// type: 'object',
|
||||
// required: ['optionA', 'optionB'],
|
||||
// properties: {
|
||||
// optionA: {
|
||||
// type: 'string'
|
||||
// },
|
||||
// optionB: {
|
||||
// type: 'string'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
type T = Static<typeof T> // type T = 'optionA' | 'optionB'
|
||||
|
||||
type R = Static<typeof R> // type R = {
|
||||
// optionA: string
|
||||
// optionB: string
|
||||
// }
|
||||
```
|
||||
|
||||
## TemplateLiteralParser
|
||||
|
||||
Template Literal types are encoded as `string` patterns. Because these types also need to act as composable union types, Revision 0.27.0 includes an expression parser / generator system specifically for regular expressions. This system is used during composition to allow templates to compose with other types, but can also be used in isolation to generate string sequences for the supported expression grammar. This functionality may be provided as standard on the `Value.*` sub module in subsequent revisions.
|
||||
|
||||
The following generates a 8-bit binary sequence for the given expression.
|
||||
|
||||
```typescript
|
||||
|
||||
import { TemplateLiteralParser, TemplateLiteralGenerator, TemplateLiteralFinite } from '@sinclair/typebox'
|
||||
|
||||
const Bit = `(0|1)` // bit union
|
||||
const Byte = `${Bit}${Bit}${Bit}${Bit}${Bit}${Bit}${Bit}${Bit}` // byte sequence
|
||||
|
||||
const E = TemplateLiteralParser.Parse(Byte) // parsed expression tree
|
||||
const F = TemplateLiteralFinite.Check(E) // is the expression finite?
|
||||
const S = [...TemplateLiteralGenerator.Generate(E)] // generate sequence
|
||||
|
||||
// const S = [ // computed sequence
|
||||
// '00000000', '00000001', '00000010', '00000011', '00000100',
|
||||
// '00000101', '00000110', '00000111', '00001000', '00001001',
|
||||
// '00001010', '00001011', '00001100', '00001101', '00001110',
|
||||
// '00001111', '00010000', '00010001', '00010010', '00010011',
|
||||
// '00010100', '00010101', '00010110', '00010111', '00011000',
|
||||
// '00011001', '00011010', '00011011', '00011100', '00011101',
|
||||
// '00011110', '00011111', '00100000', '00100001', '00100010',
|
||||
// '00100011', '00100100', '00100101', '00100110', '00100111',
|
||||
// '00101000', '00101001', '00101010', '00101011', '00101100',
|
||||
// '00101101', '00101110', '00101111', '00110000', '00110001',
|
||||
// '00110010', '00110011', '00110100', '00110101', '00110110',
|
||||
// '00110111', '00111000', '00111001', '00111010', '00111011',
|
||||
// '00111100', '00111101', '00111110', '00111111', '01000000',
|
||||
// '01000001', '01000010', '01000011', '01000100', '01000101',
|
||||
// '01000110', '01000111', '01001000', '01001001', '01001010',
|
||||
// '01001011', '01001100', '01001101', '01001110', '01001111',
|
||||
// '01010000', '01010001', '01010010', '01010011', '01010100',
|
||||
// '01010101', '01010110', '01010111', '01011000', '01011001',
|
||||
// '01011010', '01011011', '01011100', '01011101', '01011110',
|
||||
// '01011111', '01100000', '01100001', '01100010', '01100011',
|
||||
// ... 156 more items
|
||||
// ]
|
||||
```
|
||||
|
||||
<a href="Workbench"></a>
|
||||
|
||||
## Workbench
|
||||
|
||||
To assist with TypeScript alignment and to prototype new features. A new web based compiler tool has been written that allows interactive cross compiling between TypeScript and TypeBox. This tool will be enhanced seperately from the TypeBox project, but can be used to quickly generate TypeBox type definitions from existing TypeScript types.
|
||||
|
||||
[TypeBox Workbench Application](https://sinclairzx81.github.io/typebox-workbench)
|
||||
|
||||
[TypeBox Workbench Project](https://github.com/sinclairzx81/typebox-workbench)
|
||||
|
||||
<a href="https://sinclairzx81.github.io/typebox-workbench/"><img src="https://github.com/sinclairzx81/typebox-workbench/raw/main/typebox.png" /></a>
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
The following are breaking changes in Revision 0.27.0
|
||||
|
||||
<a href="TSelf-renamed-to-TThis"></a>
|
||||
|
||||
## TSelf renamed to TThis
|
||||
|
||||
This rename is to align with TypeScript interfaces. Unlike `type` aliases, TypeScript `interface` types include a implicit `this` type. This change relates specifically to TypeBox's current Recursive type which passes the `TThis` parameter via callback. The `TThis` parameter can be seen as analogous to the implicit TypeScript interface `this`.
|
||||
|
||||
Consider the following.
|
||||
|
||||
```typescript
|
||||
// type T = { id: string, nodes: this[] } // error: no implicit this
|
||||
|
||||
interface Node { // ok: this is implicit for interfaces
|
||||
id: string,
|
||||
nodes: this[]
|
||||
}
|
||||
|
||||
const T = Type.Recursive(This => // `This` === implicit 'this' for interface
|
||||
Type.Object({ //
|
||||
id: Type.String(), // Should `Recursive` be renamed to `Interface`?
|
||||
nodes: Type.Array(This)
|
||||
})
|
||||
)
|
||||
```
|
||||
Future revisions may rename `Recurisve` to `Interface`, but for now, just the `TSelf` has been renamed.
|
||||
5
changelog/0.27.1.md
Normal file
5
changelog/0.27.1.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## [0.27.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.27.1)
|
||||
|
||||
## Updates
|
||||
|
||||
- Adds a `Value.Mutate(left, right)` function. This function performs a deep mutable assignment on a value by internally remapping the `right` values on the `left`. Values omitted on the right will also be deleted on the left. This function can be useful scenarios where mutation of data is required without replacing existing reference values. An example of which might be React which tracks reference values to indicate redraw. This function is implemented by way of `ValuePointer`.
|
||||
199
changelog/0.28.0.md
Normal file
199
changelog/0.28.0.md
Normal file
@@ -0,0 +1,199 @@
|
||||
## [0.28.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.28.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.28.0 adds support for Indexed Access Types. This update also includes moderate breaking changes to Record and Composite types and does require a minor semver revision tick.
|
||||
|
||||
## Contents
|
||||
|
||||
- Enhancements
|
||||
- [Indexed Access Types](#Indexed-Access-Types)
|
||||
- [KeyOf Tuple and Array](#KeyOf-Tuple-and-Array)
|
||||
- Breaking Changes
|
||||
- [Record Types Allow Additional Properties By Default](#Record-Types-Allow-Additional-Properties-By-Default)
|
||||
- [Composite Returns Intersect for Overlapping Properties](#Composite-Returns-Intersect-for-Overlapping-Properties)
|
||||
|
||||
<a href="Indexed-Access-Types"></a>
|
||||
|
||||
## Indexed Access Types
|
||||
|
||||
Revision 0.28.0 adds [Indexed Access Type](https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html) support with a new `Type.Index()` mapping type. These types allow for deep property lookups without needing to prop dive through JSON Schema properties. This type is based on the TypeScript implementation of Indexed Access Types and allows for generalized selection of properties for complex types irrespective of if that type is a Object, Union, Intersection, Array or Tuple.
|
||||
|
||||
```typescript
|
||||
// ----------------------------------------------------------
|
||||
// The following types A and B are structurally equivalent,
|
||||
// but have varying JSON Schema representations.
|
||||
// ----------------------------------------------------------
|
||||
const A = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.String(),
|
||||
z: Type.Boolean(),
|
||||
})
|
||||
|
||||
const B = Type.Intersect([
|
||||
Type.Object({ x: Type.Number() }),
|
||||
Type.Object({ y: Type.String() }),
|
||||
Type.Object({ z: Type.Boolean() })
|
||||
])
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// TypeBox 0.27.0 - Non Uniform
|
||||
// ----------------------------------------------------------
|
||||
const A_X = A.properties.x // TNumber
|
||||
const A_Y = A.properties.y // TString
|
||||
const A_Z = A.properties.z // TBoolean
|
||||
|
||||
const B_X = B.allOf[0].properties.x // TNumber
|
||||
const B_Y = B.allOf[1].properties.y // TString
|
||||
const B_Z = B.allOf[2].properties.z // TBoolean
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// TypeBox 0.28.0 - Uniform via Type.Index
|
||||
// ----------------------------------------------------------
|
||||
const A_X = Type.Index(A, ['x']) // TNumber
|
||||
const A_Y = Type.Index(A, ['y']) // TString
|
||||
const A_Z = Type.Index(A, ['z']) // TBoolean
|
||||
|
||||
const B_X = Type.Index(B, ['x']) // TNumber
|
||||
const B_Y = Type.Index(B, ['y']) // TString
|
||||
const B_Z = Type.Index(B, ['z']) // TBoolean
|
||||
```
|
||||
Indexed Access Types support has also been extended to Tuple and Array types.
|
||||
```typescript
|
||||
// -----------------------------------------------------------
|
||||
// Array
|
||||
// -----------------------------------------------------------
|
||||
type T = string[]
|
||||
|
||||
type I = T[number] // type T = string
|
||||
|
||||
const T = Type.Array(Type.String())
|
||||
|
||||
const I = Type.Index(T, Type.Number()) // const I = TString
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Tuple
|
||||
// -----------------------------------------------------------
|
||||
type T = ['A', 'B', 'C']
|
||||
|
||||
type I = T[0 | 1] // type I = 'A' | 'B'
|
||||
|
||||
const T = Type.Array(Type.String())
|
||||
|
||||
const I = Type.Index(T, Type.Union([ // const I = TUnion<[
|
||||
Type.Literal(0), // TLiteral<'A'>,
|
||||
Type.Literal(1), // TLiteral<'B'>
|
||||
])) // ]>
|
||||
```
|
||||
|
||||
<a href="KeyOf-Tuple-and-Array"></a>
|
||||
|
||||
## KeyOf Tuple and Array
|
||||
|
||||
Revision 0.28.0 includes additional `Type.KeyOf` support for Array and Tuple types. Keys of Array will always return `TNumber`, whereas keys of Tuple will return a LiteralUnion for each index of that tuple.
|
||||
|
||||
```typescript
|
||||
// -----------------------------------------------------------
|
||||
// KeyOf: Tuple
|
||||
// -----------------------------------------------------------
|
||||
const T = Type.Tuple([Type.Number(), Type.Number(), Type.Number()])
|
||||
|
||||
const K = Type.KeyOf(T) // const K = TUnion<[
|
||||
// TLiteral<'0'>,
|
||||
// TLiteral<'1'>,
|
||||
// TLiteral<'2'>,
|
||||
// ]>
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// KeyOf: Array
|
||||
// -----------------------------------------------------------
|
||||
const T = Type.Array(Type.String())
|
||||
|
||||
const K = Type.KeyOf(T) // const K = TNumber
|
||||
```
|
||||
It is possible to combine KeyOf with Index types to extract properties from array and object constructs.
|
||||
```typescript
|
||||
// -----------------------------------------------------------
|
||||
// KeyOf + Index: Object
|
||||
// -----------------------------------------------------------
|
||||
const T = Type.Object({ x: Type.Number(), y: Type.String(), z: Type.Boolean() })
|
||||
|
||||
const K = Type.Index(T, Type.KeyOf(T)) // const K = TUnion<[
|
||||
// TNumber,
|
||||
// TString,
|
||||
// TBoolean,
|
||||
// ]>
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// KeyOf + Index: Tuple
|
||||
// -----------------------------------------------------------
|
||||
const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()])
|
||||
|
||||
const K = Type.Index(T, Type.KeyOf(T)) // const K = TUnion<[
|
||||
// TNumber,
|
||||
// TString,
|
||||
// TBoolean,
|
||||
// ]>
|
||||
```
|
||||
## Breaking Changes
|
||||
|
||||
The following are breaking changes in Revision 0.28.0
|
||||
|
||||
<a href="Record-Types-Allow-Additional-Properties-By-Default"></a>
|
||||
|
||||
## Record Types Allow Additional Properties By Default
|
||||
|
||||
Revision 0.28.0 no longer applies an automatic `additionalProperties: false` constraint to types of `TRecord`. Previously this constraint was set to prevent records with numeric keys from allowing unevaluated additional properties with non-numeric keys. This constraint worked in revisions up to 0.26.0, but since the move to use `allOf` intersect schema representations, this meant that types of Record could no longer be composed with intersections. This is due to the JSON Schema rules around extending closed schemas. Information on these rules can be found at the link below.
|
||||
|
||||
https://json-schema.org/understanding-json-schema/reference/object.html#extending-closed-schemas
|
||||
|
||||
For the most part, the omission of this constraint shouldn't impact existing record types with string keys, however numeric keys may cause problems. Consider the following where the validation unexpectedly succeeds for the following numeric keyed record.
|
||||
|
||||
```typescript
|
||||
const T = Type.Record(Type.Number(), Type.String())
|
||||
|
||||
const R = Value.Check(T, { a: null }) // true - Because `a` is non-numeric and thus is treated as an
|
||||
// additional unevaluated property.
|
||||
```
|
||||
Moving forward, Records with numeric keys "should" be constrained explicitly with `additionalProperties: false` via options if that record does not require composition through intersection. This is largely inline with the existing constraints one might apply to types of Object.
|
||||
|
||||
```typescript
|
||||
const T = Type.Record(Type.Number(), Type.String(), {
|
||||
additionalProperties: false
|
||||
})
|
||||
|
||||
const R = Value.Check(T, { a: null }) // false - Because `a` is non-numeric additional property
|
||||
```
|
||||
|
||||
<a href="Composite-Returns-Intersect-for-Overlapping-Properties"></a>
|
||||
|
||||
## Composite Returns Intersect for Overlapping Properties
|
||||
|
||||
This is a minor breaking change with respect to the schema returned for Composite objects with overlapping varying property types. Previously TypeBox would evaluate `TNever` by performing an internal `extends` check against each overlapping property type. However problems emerged using this implementation for users who needed to use Composite with types of `TUnsafe`. This is due to unsafe types being incompatible with TypeBox's internal extends logic.
|
||||
|
||||
The solution implemented in 0.28.0 is to return the full intersection of all overlapping properties. The reasoning here is that if the overlapping properties of varying types result in an illogical intersection, this is semantically the same as resolving `never` for that property. This approach avoids the need to internally check if all overlapping properties extend or narrow one another.
|
||||
|
||||
```typescript
|
||||
const T = Type.Composite([
|
||||
Type.Object({ x: Type.Number() }), // overlapping property 'x' of varying type
|
||||
Type.Object({ x: Type.String() })
|
||||
])
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Revision 0.27.0
|
||||
// -----------------------------------------------------------
|
||||
const R = Type.Object({
|
||||
x: Type.Never() // Never evaluated through extends checks.
|
||||
})
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Revision 0.28.0
|
||||
// -----------------------------------------------------------
|
||||
const R = Type.Object({
|
||||
x: Type.Intersect([ // Illogical intersections are semantically the same as never
|
||||
Type.Number(),
|
||||
Type.String()
|
||||
])
|
||||
})
|
||||
```
|
||||
This implementation should make it more clear what the internal mechanics are for object compositing. Future revisions of TypeBox may however provide a utility function to test illogical intersections for Never for known types.
|
||||
57
changelog/0.28.3.md
Normal file
57
changelog/0.28.3.md
Normal file
@@ -0,0 +1,57 @@
|
||||
## [0.28.3](https://www.npmjs.com/package/@sinclair/typebox/v/0.28.3)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.28.3 adds a new Rest type to support variadic type composition.
|
||||
|
||||
## Contents
|
||||
|
||||
- Enhancements
|
||||
- [Variadic Types](#Variadic-Types)
|
||||
|
||||
<a href="Variadic-Types"></a>
|
||||
|
||||
## Variadic Types
|
||||
|
||||
Revision 0.28.3 adds a new type named `Type.Rest`. This type is used to extract a tuple array of type of `[...TSchema]`. The return value of this type is not strictly JSON Schema, however the tuple array can be used as a parameter to other types that accept tuples as their arguments.
|
||||
|
||||
### Tuple Concatenation
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
|
||||
type A = [1, 2]
|
||||
|
||||
type B = [3, 4]
|
||||
|
||||
type C = [...A, ...B]
|
||||
|
||||
// TypeBox
|
||||
|
||||
const A = Type.Tuple([Type.Literal(1), Type.Literal(2)])
|
||||
|
||||
const B = Type.Tuple([Type.Literal(3), Type.Literal(4)])
|
||||
|
||||
const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)])
|
||||
```
|
||||
|
||||
### Tuple To Parameter
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
|
||||
type P = [number, number]
|
||||
|
||||
type F1 = (param: [...P]) => void
|
||||
|
||||
type F2 = (param: [...P, number]) => void
|
||||
|
||||
// TypeBox
|
||||
|
||||
const P = Type.Tuple([Type.Number(), Type.Number()])
|
||||
|
||||
const F1 = Type.Function(Type.Rest(P), Type.Void())
|
||||
|
||||
const F2 = Type.Function([...Type.Rest(P), Type.Number()], Type.Void())
|
||||
```
|
||||
|
||||
144
changelog/0.29.0.md
Normal file
144
changelog/0.29.0.md
Normal file
@@ -0,0 +1,144 @@
|
||||
## [0.29.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.29.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.29.0 makes a minor interface and schema representation change to the `Type.Not` type. This revision also includes a fix for indexed access types on TypeScript 5.1.6.
|
||||
|
||||
As this revision constitutes a breaking representation change for `Type.Not`, a minor semver revision is required.
|
||||
|
||||
## Contents
|
||||
|
||||
- Enhancements
|
||||
- [Type.Not Representation Change](#Representation-Change)
|
||||
- [Not Inversion](#Not-Inversion)
|
||||
- [Inference Limitations](#Inference-Limitations)
|
||||
|
||||
<a name="Representation-Change"></a>
|
||||
|
||||
## Type.Not Representation Change
|
||||
|
||||
The `Type.Not` was first introduced in Revision 0.26.0. This type accepted two arguments, the first is the `not` type, the second is the `allowed` type. In 0.26.0, TypeBox would treat the `allowed` type as the inferred type with the schema represented in the following form.
|
||||
|
||||
### 0.26.0
|
||||
|
||||
```typescript
|
||||
// allow all numbers except the number 42
|
||||
//
|
||||
const T = Type.Not(Type.Literal(42), Type.Number())
|
||||
// ^ ^
|
||||
// not type allowed type
|
||||
|
||||
// represented as
|
||||
//
|
||||
const T = {
|
||||
allOf: [
|
||||
{ not: { const: 42 } },
|
||||
{ type: 'number' }
|
||||
]
|
||||
}
|
||||
|
||||
// inferred as
|
||||
//
|
||||
type T = Static<typeof T> // type T = number
|
||||
```
|
||||
In 0.26.0. the rationale for the second `allowed` argument was provide a correct static type to infer, where one could describe what the type wasn't on the first and what it was on the second (with inference of operating on the second argument). This approach was to echo possible suggestions for negated type syntax in TypeScript.
|
||||
|
||||
```typescript
|
||||
type T = number & not 42 // not actual typescript syntax!
|
||||
```
|
||||
|
||||
### 0.29.0
|
||||
|
||||
Revision 0.29.0 changes the `Type.Not` type to take a single `not` argument only. This type statically infers as `unknown`
|
||||
|
||||
```typescript
|
||||
// allow all types except the literal number 42
|
||||
//
|
||||
const T = Type.Not(Type.Literal(42))
|
||||
// ^
|
||||
// not type
|
||||
|
||||
// represented as
|
||||
//
|
||||
const T = { not: { const: 42 } }
|
||||
|
||||
// inferred as
|
||||
//
|
||||
type T = Static<typeof T> // type T = unknown
|
||||
|
||||
```
|
||||
### Upgrading to 0.29.0
|
||||
|
||||
In revision 0.29.0, you can express the 0.26.0 Not type via `Type.Intersect` which explicitly creates the `allOf` representation. The type inference works in this case as intersected `number & unknown` yields the most narrowed type (which is `number`)
|
||||
|
||||
```typescript
|
||||
// allow all numbers except the number 42
|
||||
//
|
||||
const T = Type.Intersect([ Type.Not(Type.Literal(42)), Type.Number() ])
|
||||
// ^ ^
|
||||
// not type allowed type
|
||||
|
||||
// represented as
|
||||
//
|
||||
const T = {
|
||||
allOf: [
|
||||
{ not: { const: 42 } },
|
||||
{ type: 'number' }
|
||||
]
|
||||
}
|
||||
// inferred as
|
||||
//
|
||||
type T = Static<typeof T> // type T = number
|
||||
```
|
||||
The 0.29.0 `Not` type properly represents the JSON Schema `not` keyword in its simplest form, as well as making better use of intersection type narrowing capabilities of TypeScript.
|
||||
|
||||
<a name="Not-Inversion"></a>
|
||||
|
||||
## Not Inversion
|
||||
|
||||
The not type can be inverted through nesting.
|
||||
|
||||
```typescript
|
||||
// not not string
|
||||
//
|
||||
const T = Type.Not(Type.Not(Type.String()))
|
||||
|
||||
// represented as
|
||||
//
|
||||
const T = {
|
||||
not: {
|
||||
not: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// inferred as
|
||||
//
|
||||
type T = Static<typeof T> // type T = string
|
||||
```
|
||||
|
||||
<a name="Inference-Limitations"></a>
|
||||
|
||||
## Inference Limitations
|
||||
|
||||
Not types are synonymous with the concept of [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. Because of this, it is not currently possible to infer negated types in a way one would naturally expect for some cases. Consider the following.
|
||||
|
||||
```typescript
|
||||
const T = Type.Intersect([Type.String(), Type.Not(Type.String())])
|
||||
|
||||
type T = Static<typeof T> // type T = string & not string
|
||||
// actual: string
|
||||
// expect: never
|
||||
```
|
||||
As such, the use of Not types should be used with some consideration to current limitations, and reserved primarily for narrowing cases such as the following.
|
||||
|
||||
```typescript
|
||||
const T = Type.Intersect([Type.String(), Type.Not(Type.Literal('disallowed string'))])
|
||||
|
||||
type T = Static<typeof T> // type T = string & not 'disallowed string'
|
||||
// actual: string
|
||||
// expect: string
|
||||
```
|
||||
|
||||
|
||||
141
changelog/0.29.2.md
Normal file
141
changelog/0.29.2.md
Normal file
@@ -0,0 +1,141 @@
|
||||
## [0.29.2](https://www.npmjs.com/package/@sinclair/typebox/v/0.29.2)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.29.2 includes enhancements to `Type.Index`
|
||||
|
||||
This revision contains no breaking changes
|
||||
|
||||
## Contents
|
||||
|
||||
- Enhancements
|
||||
- [Modifier Index Resolver](#Modifier-Index-Resolver)
|
||||
- [Indexed Intersect](#Indexed-Intersect)
|
||||
- [Indexed Union](#Indexed-Union)
|
||||
- [Composite](#Composite)
|
||||
|
||||
<a name="Modifier-Index-Resolver"></a>
|
||||
|
||||
## Modifier Index Resolver
|
||||
|
||||
Revision 0.29.2 re-introduces optional property narrowing for Indexed Access Types. This functionality is specific when indexing overlapping properties with one or more optional modifiers. Revision 0.28.0 attempted to implement this narrowing, however was pulled due to instantiation issues raised on issue [419](https://github.com/sinclairzx81/typebox/issues/419) (specific to composite narrowing)
|
||||
|
||||
Revision 0.29.2 attempts to re-introduce this functionality using a different resolution strategy. It uses Indexed Access Types as the construct in which to apply such narrowing and expands upon Union and Intersection type normalization for optional modifier unwrap and remapping. This approach unifies Composite inference with Index Access Types and makes provisions for a possible `Type.Mapped` feature in later releases.
|
||||
|
||||
<a name="Indexed-Intersect"></a>
|
||||
|
||||
## Indexed Intersect
|
||||
|
||||
The following are the index resolver cases for intersect types.
|
||||
|
||||
### Case 1
|
||||
```typescript
|
||||
const T = Type.Intersect([
|
||||
Type.Object({ x: Type.Optional(Type.Number()) }),
|
||||
Type.Object({ x: Type.Optional(Type.Number()) })
|
||||
])
|
||||
|
||||
const I = Type.Index(T, ['x'])
|
||||
|
||||
// type I = TOptional<TUnion<[TNumber, TNumber]>>
|
||||
```
|
||||
### Case 2
|
||||
```typescript
|
||||
const T = Type.Intersect([
|
||||
Type.Object({ x: Type.Optional(Type.Number()) }),
|
||||
Type.Object({ x: Type.Number() })
|
||||
])
|
||||
|
||||
const I = Type.Index(T, ['x'])
|
||||
|
||||
// type I = TUnion<[TNumber, TNumber]>
|
||||
```
|
||||
### Case 3
|
||||
```typescript
|
||||
const T = Type.Intersect([
|
||||
Type.Object({ x: Type.Number() }),
|
||||
Type.Object({ x: Type.Number() })
|
||||
])
|
||||
|
||||
const I = Type.Index(T, ['x'])
|
||||
|
||||
// type I = TUnion<[TNumber, TNumber]>
|
||||
```
|
||||
<a name="Indexed-Union"></a>
|
||||
|
||||
## Indexed Union
|
||||
|
||||
The following are the index resolver cases for union types.
|
||||
|
||||
### Case 1
|
||||
```typescript
|
||||
const T = Type.Union([
|
||||
Type.Object({ x: Type.Optional(Type.Number()) }),
|
||||
Type.Object({ x: Type.Optional(Type.Number()) })
|
||||
])
|
||||
|
||||
const I = Type.Index(T, ['x'])
|
||||
|
||||
// type I = TOptional<TUnion<[TNumber, TNumber]>>
|
||||
```
|
||||
### Case 2
|
||||
```typescript
|
||||
const T = Type.Union([
|
||||
Type.Object({ x: Type.Optional(Type.Number()) }),
|
||||
Type.Object({ x: Type.Number() })
|
||||
])
|
||||
|
||||
const I = Type.Index(T, ['x'])
|
||||
|
||||
// type I = TOptional<TUnion<[TNumber, TNumber]>>
|
||||
```
|
||||
### Case 3
|
||||
```typescript
|
||||
const T = Type.Union([
|
||||
Type.Object({ x: Type.Number() }),
|
||||
Type.Object({ x: Type.Number() })
|
||||
])
|
||||
|
||||
const I = Type.Index(T, ['x'])
|
||||
|
||||
// type I = TUnion<[TNumber, TNumber]>
|
||||
```
|
||||
<a name="Composite"></a>
|
||||
|
||||
## Composite
|
||||
|
||||
The following are the resolver cases for indexed types when applied to composite intersection.
|
||||
|
||||
### Case 1
|
||||
```typescript
|
||||
const T = Type.Composite([
|
||||
Type.Object({ x: Type.Optional(Type.Number()) }),
|
||||
Type.Object({ x: Type.Optional(Type.Number()) })
|
||||
])
|
||||
|
||||
// type T = TObject<{
|
||||
// x: TOptional<TUnion<[TNumber, TNumber]>>
|
||||
// }>
|
||||
```
|
||||
### Case 2
|
||||
```typescript
|
||||
const T = Type.Composite([
|
||||
Type.Object({ x: Type.Optional(Type.Number()) }),
|
||||
Type.Object({ x: Type.Number() })
|
||||
])
|
||||
|
||||
// type T = TObject<{
|
||||
// x: TUnion<[TNumber, TNumber]>
|
||||
// }>
|
||||
```
|
||||
### Case 3
|
||||
```typescript
|
||||
const T = Type.Composite([
|
||||
Type.Object({ x: Type.Number() }),
|
||||
Type.Object({ x: Type.Number() })
|
||||
])
|
||||
|
||||
// type T = TObject<{
|
||||
// x: TUnion<[TNumber, TNumber]>
|
||||
// }>
|
||||
```
|
||||
419
changelog/0.30.0.md
Normal file
419
changelog/0.30.0.md
Normal file
@@ -0,0 +1,419 @@
|
||||
## [0.30.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.30.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.30.0 is a milestone revision for the TypeBox project. It is primarily focused on internal optimizations, refactoring work to reduce package and bundle sizes, enable increased modularity of internal sub modules (with some considerations given to future ESM publishing), renaming internal functions to address react native bundling issues and consolidating shared internal modules to reduce overall code overhead.
|
||||
|
||||
This revision also implements several new features, including new validation constraints for Array, new types for iterators, new utility types, a TypeScript code generation option for the compiler, enhancements made to modifiers and better options for TypeScript to TypeBox code translation. This revision also includes new examples including a transform type for handling IO encode an decode as well as a reference implementation for JSON Type Definition specification.
|
||||
|
||||
This revision includes breaking changes and some deprecations. It requires a minor semver revision.
|
||||
|
||||
## Contents
|
||||
|
||||
- Enhancements
|
||||
- [TypeScript Code Generation](#TypeScript-Code-Generation)
|
||||
- [Optional and Readonly](#Optional-and-Readonly)
|
||||
- [Iterator and AsyncIterator](#Iterator-and-AsyncIterator)
|
||||
- [Order Independent References](#Order-Independent-References)
|
||||
- [Value Submodules](#Value-Submodules)
|
||||
- [Array Contains Constraint](#Array-Contains-Constraint)
|
||||
- [Additional Utility Types](#Additional-Utility-Types)
|
||||
- [Reduced Package Size](#Reduced-Package-Size)
|
||||
- [TypeBox Codegen](#TypeBox-Codegen)
|
||||
- Examples
|
||||
- [JSON Type Definition](#JSON-Type-Definition)
|
||||
- [Prototype Types](#Prototype-Types)
|
||||
- [Transform Types](#Transform-Types)
|
||||
- Breaking
|
||||
- [Extended Type Representation-Change](#Extended-Type-Representation-Change)
|
||||
- [Modifier Symbol Deprecated](#Modifier-Deprecated)
|
||||
- [RegEx Renamed To RegExp](#RegEx-Renamed-To-RegExp)
|
||||
- [ValueErrorType Custom Renamed To Kind](#ValueErrorType-Custom-Renamed-To-Kind)
|
||||
|
||||
<a name="TypeScript Code Generation"></a>
|
||||
|
||||
## TypeScript Code Generation
|
||||
|
||||
Revision 0.30.0 adds TypeScript code generation support to the TypeCompiler. By specifying the language option on the `.Code()` function, TypeBox will add type annotations to the compiled output. This functionality can be used to produce typed TS functions for projects that preference AOT compilation.
|
||||
|
||||
```typescript
|
||||
const Code = TypeCompiler.Code(Type.String(), { // return function check(value: any): boolean {
|
||||
language: 'typescript' // return (
|
||||
}) // (typeof value === 'string')
|
||||
// )
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Optional-and-Readonly"></a>
|
||||
|
||||
## Optional and Readonly
|
||||
|
||||
Revision 0.30.0 deprecates the `[Modifier]` symbol and introduces two new symbols, `[Readonly]` and `[Optional]`. This change is carried out to simplify type inference as well as to simplify runtime mapping logic. This change should not implicate users leveraging the `Type.*` purely for type composition, however implementors using TypeBox for reflection and code generation should update to the new symbols.
|
||||
|
||||
```typescript
|
||||
// Revision 0.29.0
|
||||
//
|
||||
const A = Type.ReadonlyOptional(Type.Number()) // const A: TReadonlyOptional<TNumber> = {
|
||||
// type: 'number',
|
||||
// [TypeBox.Modifier]: 'ReadonlyOptional'
|
||||
// }
|
||||
|
||||
const B = Type.Readonly(Type.Number()) // const B: TReadonly<TNumber> = {
|
||||
// type: 'number',
|
||||
// [TypeBox.Modifier]: 'Readonly'
|
||||
// }
|
||||
|
||||
const C = Type.Optional(Type.Number()) // const C: TOptional<TNumber> = {
|
||||
// type: 'number',
|
||||
// [TypeBox.Modifier]: 'Optional'
|
||||
// }
|
||||
|
||||
// Revision 0.30.0
|
||||
//
|
||||
const A = Type.ReadonlyOptional(Type.Number()) // const A: TReadonly<TOptional<TNumber>> = {
|
||||
// type: 'number',
|
||||
// [TypeBox.Readonly]: 'Readonly',
|
||||
// [TypeBox.Optional]: 'Optional'
|
||||
// }
|
||||
|
||||
const B = Type.Readonly(Type.Number()) // const B: TReadonly<TNumber> = {
|
||||
// type: 'number',
|
||||
// [TypeBox.Readonly]: 'Readonly'
|
||||
// }
|
||||
|
||||
const C = Type.Optional(Type.Number()) // const C: TOptional<TNumber> = {
|
||||
// type: 'number',
|
||||
// [TypeBox.Optional]: 'Optional'
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Iterator-and-AsyncIterator"></a>
|
||||
|
||||
## Iterator and AsyncIterator
|
||||
|
||||
Revision 0.30.0 adds the types `Iterator` and `AsyncIterator`. These types add to the existing non-validatable extended type set and can be used build callable generator functions. These types are written primarily to describe RPC network interfaces that return multiple values. Examples of which may include web socket streams or reading database result cursors over a network.
|
||||
|
||||
```typescript
|
||||
// Revision 0.30.0
|
||||
//
|
||||
const Enumerable = <T extends TSchema>(T: T) => Type.Function([
|
||||
Type.Number({ description: 'Start index' }),
|
||||
Type.Number({ description: 'End index' })
|
||||
], Type.Iterator(T))
|
||||
|
||||
const EnumerableNumber = Enumerable(Type.Number())
|
||||
|
||||
const Range: Static<typeof EnumerableNumber> = function * (start: number, end: number) {
|
||||
for(let i = start; i < end; i++) yield i
|
||||
}
|
||||
|
||||
const R = [...Range(10, 20)] // const R = [10, 11, 12, ..., 19]
|
||||
```
|
||||
|
||||
<a name="Order-Independent-References"></a>
|
||||
|
||||
## Order Independent References
|
||||
|
||||
Revision 0.30.0 adds an overload for `Ref` to enable non order dependent type referencing. Prior to this revision, reference targets needed to be defined first before being referenced. Revision 0.30.0 lifts this restriction and allows referencing of "yet to be defined" targets through the use of `typeof` operator. This overload borrows on TypeScript's ability to derive type information irrespective of topological ordering.
|
||||
|
||||
This overload is implemented for "TypeScript to TypeBox" code generation utilities where TypeScript types are not guaranteed ordered in a runtime sorted fashion.
|
||||
|
||||
```typescript
|
||||
// Revision 0.29.0
|
||||
//
|
||||
const R = Type.Ref(T) // Error: T isn't defined yet
|
||||
|
||||
const T = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number()
|
||||
}, { $id: 'T' })
|
||||
|
||||
// Revision 0.30.0
|
||||
//
|
||||
const R = Type.Ref<typeof T>('T') // Ok: infer from typeof T
|
||||
|
||||
const T = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number()
|
||||
}, { $id: 'T' })
|
||||
```
|
||||
|
||||
<a name="Value-Submodules"></a>
|
||||
|
||||
## Value Submodules
|
||||
|
||||
Revision 0.30.0 carries out a number of refactorings for the `Value.*` modules to enable each submodule to be imported individually. These refactorings are support better "pay to play" library characteristics, allowing users to import only the submodules they need. This update also makes provisions for ESM publishing by removing internal namespaces.
|
||||
|
||||
The top level `Value.*` namespace will remain on all subsequent versions of TypeBox.
|
||||
|
||||
```typescript
|
||||
// Revision 0.29.0
|
||||
//
|
||||
import { Value } from '@sinclair/typebox/value' // Value.* namespace
|
||||
|
||||
const A = Value.Create(Type.String())
|
||||
|
||||
// Revision 0.30.0
|
||||
//
|
||||
import { Create } from '@sinclair/typebox/value/create' // Only Create()
|
||||
|
||||
const A = Create(Type.String())
|
||||
```
|
||||
|
||||
<a name="Array-Contains-Constraint"></a>
|
||||
|
||||
## Array Contains Constraint
|
||||
|
||||
Revision 0.30.0 implements validation support for the `contains` keyword as well as the draft 2019-09 `minContains` and `maxContains` constraints on `Array`. Documentation on these constraints can be found https://json-schema.org/understanding-json-schema/reference/array.html#contains
|
||||
|
||||
```typescript
|
||||
// Revision 0.30.0
|
||||
//
|
||||
const T = Type.Array(Type.Number(), {
|
||||
contains: Type.Literal(1),
|
||||
minContains: 3,
|
||||
maxContains: 5
|
||||
})
|
||||
|
||||
Value.Check(T, [1, 1, 1]) // true - between 3 and 5 instances of 1
|
||||
Value.Check(T, [1, 1, 1, 1, 1]) // true - between 3 and 5 instances of 1
|
||||
Value.Check(T, [0, 1, 1, 1, 1, 1]) // true - between 3 and 5 instances of 1
|
||||
Value.Check(T, [1, 1]) // false - less than 3 instances of 1
|
||||
Value.Check(T, [1, 1, 1, 1, 1, 1]) // false - more than 5 instances of 1
|
||||
Value.Check(T, [0]) // false - no instances of 1
|
||||
```
|
||||
|
||||
<a name="Additional-Utility-Types"></a>
|
||||
|
||||
## Additional Utility Types
|
||||
|
||||
Revision 0.30.0 adds the utility types `Awaited`, `Uppercase`, `Lowercase`, `Capitalize`, and `Uncapitalize` to the supported type set.
|
||||
|
||||
```typescript
|
||||
// Revision 0.30.0
|
||||
const T1 = Type.Awaited(Type.Promise(Type.String())) // const T1: TString
|
||||
|
||||
const T2 = Type.Uppercase(Type.Literal('hello')) // const T2: TLiteral<'HELLO'>
|
||||
|
||||
const T3 = Type.Lowercase(Type.Literal('HELLO')) // const T3: TLiteral<'hello'>
|
||||
|
||||
const T4 = Type.Capitalize(Type.Literal('hello')) // const T4: TLiteral<'Hello'>
|
||||
|
||||
const T5 = Type.Uncapitalize(Type.Literal('HELLO')) // const T5: TLiteral<'hELLO'>
|
||||
```
|
||||
A full list of TypeScript utility types can be found at this [link](https://www.typescriptlang.org/docs/handbook/utility-types.html).
|
||||
|
||||
<a name="Reduced-Package-Size"></a>
|
||||
|
||||
## Reduced Package Size
|
||||
|
||||
Revision 0.30.0 carries out several internal refactorings to reduce package and bundle sizes. This work is largely an ongoing process with provisional work carried out across `type`, `value` and `compiler` modules. Revision 0.30.0 manages to weigh in slightly less than Revision 0.29.0 with the additional functionality provided on the revision.
|
||||
|
||||
```typescript
|
||||
// Revision 0.29.0
|
||||
//
|
||||
┌──────────────────────┬────────────┬────────────┬─────────────┐
|
||||
│ (index) │ Compiled │ Minified │ Compression │
|
||||
├──────────────────────┼────────────┼────────────┼─────────────┤
|
||||
│ typebox/compiler │ '130.3 kb' │ ' 58.2 kb' │ '2.24 x' │
|
||||
│ typebox/errors │ '113.3 kb' │ ' 49.8 kb' │ '2.27 x' │
|
||||
│ typebox/system │ ' 78.8 kb' │ ' 32.2 kb' │ '2.45 x' │
|
||||
│ typebox/value │ '180.0 kb' │ ' 77.7 kb' │ '2.32 x' │
|
||||
│ typebox │ ' 77.7 kb' │ ' 31.7 kb' │ '2.45 x' │
|
||||
└──────────────────────┴────────────┴────────────┴─────────────┘
|
||||
|
||||
// Revision 0.30.0
|
||||
//
|
||||
┌──────────────────────┬────────────┬────────────┬─────────────┐
|
||||
│ (index) │ Compiled │ Minified │ Compression │
|
||||
├──────────────────────┼────────────┼────────────┼─────────────┤
|
||||
│ typebox/compiler │ '129.4 kb' │ ' 58.6 kb' │ '2.21 x' │
|
||||
│ typebox/errors │ '111.6 kb' │ ' 50.1 kb' │ '2.23 x' │
|
||||
│ typebox/system │ ' 76.5 kb' │ ' 31.7 kb' │ '2.41 x' │
|
||||
│ typebox/value │ '180.7 kb' │ ' 79.3 kb' │ '2.28 x' │
|
||||
│ typebox │ ' 75.4 kb' │ ' 31.3 kb' │ '2.41 x' │
|
||||
└──────────────────────┴────────────┴────────────┴─────────────┘
|
||||
```
|
||||
|
||||
<a name="TypeBox-Codegen"></a>
|
||||
|
||||
## TypeBox Codegen
|
||||
|
||||
Revision 0.30.0 offers an external code generation API tool which can be used to programmatically convert TypeScript types into TypeBox types.
|
||||
|
||||
[TypeBox-Code Project](https://github.com/sinclairzx81/typebox-codegen)
|
||||
|
||||
```typescript
|
||||
import * as Codegen from '@sinclair/typebox-codegen'
|
||||
|
||||
const Code = Codegen.TypeScriptToTypeBox.Generate(`
|
||||
type T = { x: number, y: number, z: number }
|
||||
`)
|
||||
|
||||
console.log(Code)
|
||||
|
||||
// Output:
|
||||
//
|
||||
// import { Type, Static } from '@sinclair/typebox'
|
||||
//
|
||||
// type T = Static<typeof T>
|
||||
// const T = Type.Object({
|
||||
// x: Type.Number(),
|
||||
// y: Type.Number(),
|
||||
// z: Type.Number()
|
||||
// })
|
||||
```
|
||||
|
||||
<a name="JSON-Type-Definition"></a>
|
||||
|
||||
## JSON Type Definition
|
||||
|
||||
Revision 0.30.0 includes a reference implementation for JSON Type Definition (RFC 8927). This specification is currently under consideration for inclusion in the TypeBox library as an alternative schema representation for nominal type systems. The implementation currently contains all types expressed in the JSON Type Definition spec, but omits constraints such and `minimum` and `maximum` values (which are not formally represented in the specification).
|
||||
|
||||
The implementation is offered as a single file which can be copied in to projects with TypeBox installed. This implementation may be enhanced over the next few revisions (with some potential to implement mapping types such as partial, required, omit, pick, keyof). This specification will be considered for inclusion under `@sinclair/typebox/typedef` if there is enough interest.
|
||||
|
||||
```typescript
|
||||
import { Type } from './typedef' // from: examples/typedef/typedef.ts
|
||||
|
||||
const T3 = Type.Struct({ // const T3 = {
|
||||
x: Type.Float32(), // properties: {
|
||||
y: Type.Float32(), // x: { type: 'float32' },
|
||||
z: Type.Float32() // y: { type: 'float32' },
|
||||
}) // z: { type: 'float32' }
|
||||
// }
|
||||
// }
|
||||
|
||||
const T2 = Type.Struct({ // const T3 = {
|
||||
x: Type.Float32(), // properties: {
|
||||
y: Type.Float32() // x: { type: 'float32' },
|
||||
}) // y: { type: 'float32' }
|
||||
// }
|
||||
// }
|
||||
|
||||
const U = Type.Union([ // const U = {
|
||||
T3, // discriminator: 'type',
|
||||
T2 // mapping: {
|
||||
]) // 0: {
|
||||
// properties: {
|
||||
// x: { type: 'float32' },
|
||||
// y: { type: 'float32' },
|
||||
// z: { type: 'float32' }
|
||||
// }
|
||||
// },
|
||||
// 1: {
|
||||
// properties: {
|
||||
// x: { type: 'float32' },
|
||||
// y: { type: 'float32' }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Prototype-Types"></a>
|
||||
|
||||
## Prototype Types
|
||||
|
||||
Revision 0.30.0 renames `Experimental` types to `Prototype` types within the examples directory. Updates here include additional documentation and rationales for the existing types `UnionOneOf`, `UnionEnum`, `Const`, and includes two new types `Evaluate` and `PartialDeep`. These types are written as standalone modules and can be copied into a project for direct use. The TypeBox project is open to community discussions around the inclusion of these types in future revisions.
|
||||
|
||||
<a name="Transform-Types"></a>
|
||||
|
||||
## Transform Types
|
||||
|
||||
Revision 0.30.0 provides a reference implementation for Transform types. There has been some interest from users to offer combinators similar to Zod's `.transform()` function that permits remapping values during `.parse()` like operations. As TypeBox types do not have fluent combinators or a parse function (and are just JSON Schema objects), introducing similar functionality without augmenting types or implementing a `.parse()` on all types has proven to be particularily challenging.
|
||||
|
||||
The reference Transform implementation implements a workable design by augmenting TypeBox types with codec functions outside the type system. These functions allow values to be structurally encoded and decoded through the `.parseLike()` functions `Encode()` and `Decode()`. TypeBox adopts the `io-ts` perspective for value transformation, viewing the act of transforming values primarily the role of dedicated codec system. As much of this functionality is considered high level and above and beyond the type system, Transform types will not likely be added to TypeBox type system; but rather added as an optional import in later revisions.
|
||||
|
||||
```typescript
|
||||
import { Transform, Encode, Decode } from './transform'
|
||||
|
||||
const Timestamp = Transform(Type.Number(), { // The Transform function wraps a TypeBox type with two codec
|
||||
decode: (value) => new Date(value), // functions which implement logic to decode a received value
|
||||
encode: (value) => value.getTime(), // (i.e. number) into a application type (Date). The encode
|
||||
}) // function handles the reverse mapping.
|
||||
|
||||
type N = Static<typeof N> // type N = { timestamp: number }
|
||||
//
|
||||
const N = Type.Object({ // Transform types are to be used like any other type and will
|
||||
timestamp: Timestamp // infer as the original TypeBox type. For example, the type `N`
|
||||
}) // above will infer as { timestamp: number } (as derived from
|
||||
// the TB type)
|
||||
|
||||
|
||||
|
||||
const D = Decode(N, { timestamp: 123 }) // const D = { timestamp: Date(123) }
|
||||
//
|
||||
// The Decode function accepts any type plus a value. The Decode
|
||||
// function return type will be that of the transforms decode()
|
||||
// return type (which is Date), with the second argument statically
|
||||
// typed as N. This function acts as a kind of parse() that returns
|
||||
// the decoded type or throws on validation error.
|
||||
|
||||
|
||||
const E = Encode(N, { timestamp: new Date(123) }) // const E = { timestamp: 123 }
|
||||
//
|
||||
// The encode function performs the inverse, accepting the
|
||||
// decoded type { timestamp: Date } and re-encoding to the
|
||||
// target type { timestamp: number }. This function will
|
||||
// also throw on validation error.
|
||||
```
|
||||
|
||||
<a name="Extended-Type-Representation-Change"></a>
|
||||
|
||||
## Extended Type Representation Change
|
||||
|
||||
Revision 0.30.0 updates representations for all extended types. This change is made due to TypeBox's observed role as a general purpose JavaScript validation library as well as to deprecate support for extended type validation in Ajv which was only ever partially functional at best.
|
||||
|
||||
Attempts were made on Revision 0.25.0 to restructure extended types to provide Ajv hooks for custom type configuration. These hooks used the `type` property where `{ type: 'object', instanceOf: 'Type' }` was used to configure schematics for JavaScript objects, and `{ type: 'null', typeOf: 'Type' }` was used for JavaScript primitives. Despite these hooks, Ajv would still struggle with validation of primitive types (such as `undefined`), and for the types `Function`, `Constructor` and `Promise`; these were meaningless to Ajv and it did not make sense to try provide hooks for a validator that could not make use of them.
|
||||
|
||||
This change represents a move towards a formal specification to express pure JavaScript constructs which is partially under discussion within the runtime type community. This change will implicate the use of `Uint8Array` and `Date` objects when configuring for Ajv. A supplimentary fallback will be provided in the `/examples` directory using `Type.Unsafe`
|
||||
|
||||
```typescript
|
||||
// Revision 0.29.0
|
||||
//
|
||||
const T = Type.Date() // const T: TDate = { type: 'object', instanceOf: 'Date' }
|
||||
|
||||
const U = Type.Undefined() // const U: TUndefined = { type: 'null', typeOf: 'Undefined' }
|
||||
|
||||
// Revision 0.30.0
|
||||
//
|
||||
const T = Type.Date() // const T: TDate = { type: 'Date' }
|
||||
|
||||
const U = Type.Undefined() // const U: TUndefined = { type: 'undefined' }
|
||||
```
|
||||
|
||||
<a name="RegEx-Renamed-To-RegExp"></a>
|
||||
|
||||
## RegEx Renamed To RegExp
|
||||
|
||||
Revision 0.30.0 marks `Type.RegEx` as deprecated but provides `Type.RegExp` as an alternative (matching the JavaScript `RegExp` type name). Additionally this type has also been moved from the `Standard` to `Extended` type set. The `RegExp` type will no longer considered part of the Standard type set due to JavaScript Regular Expressions supporting a wider range of symbols and control characeters than is supported by the ECMA262 subset used by the JSON Schema specification. Information on the ECMA262 subset supported by JSON Schema can be found at the following Url https://json-schema.org/understanding-json-schema/reference/regular_expressions.html
|
||||
|
||||
As `Type.RegEx()` is widely used, this function will be retained under the `@deprecated` annotation for the 0.30.0 revision.
|
||||
|
||||
```typescript
|
||||
// Revision 0.29.0
|
||||
|
||||
const T = Type.RegEx(/abc/)
|
||||
|
||||
// Revision 0.30.0
|
||||
|
||||
const A = Type.RegEx(/abc/) // deprecation warning!
|
||||
|
||||
const B = Type.RegExp(/abc/) // Extended Type
|
||||
|
||||
const T = Type.String({ pattern: /abc/.source }) // Standard Type
|
||||
```
|
||||
For Unicode (UTF-16) support on 0.30.0, the recommendation is to continue using user defined formats.
|
||||
|
||||
```typescript
|
||||
import { Type, FormatRegistry } from '@sinclair/typebox'
|
||||
|
||||
FormatRegistry.Set('emoji', value => /<a?:.+?:\d{18}>|\p{Extended_Pictographic}/gu.test(value))
|
||||
|
||||
const T = Type.String({ format: 'emoji' })
|
||||
|
||||
Value.Check(T, '♥️♦️♠️♣️') // Ok
|
||||
```
|
||||
For information on configuring custom formats on Ajv, refer to https://ajv.js.org/guide/formats.html#user-defined-formats
|
||||
331
changelog/0.31.0.md
Normal file
331
changelog/0.31.0.md
Normal file
@@ -0,0 +1,331 @@
|
||||
## [0.31.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.31.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.31.0 is a subsequent milestone revision for the TypeBox library and a direct continuation of the work carried out in 0.30.0 to optimize and prepare TypeBox for a 1.0 release candidate. This revision implements a new codec system with Transform types, provides configurable error message generation for i18n support, adds a library wide exception type named TypeBoxError and generalizes the Rest type to enable richer composition. This revision also finalizes optimization work to reduce the TypeBox package size.
|
||||
|
||||
This revision contains relatively minor breaking changes due to internal type renaming. A minor semver revision is required.
|
||||
|
||||
## Contents
|
||||
|
||||
- Enhancements
|
||||
- [Transform Types](#Transform-Types)
|
||||
- [Encode and Decode](#Encode-and-Decode)
|
||||
- [StaticEncode and StaticDecode](#StaticEncode-and-StaticDecode)
|
||||
- [Rest Types](#Rest-Types)
|
||||
- [Record Key](#Record-Key)
|
||||
- [TypeBoxError](#TypeBoxError)
|
||||
- [TypeSystemErrorFunction](#TypeSystemErrorFunction)
|
||||
- [Reduce Package Size](#Reduce-Package-Size)
|
||||
- Breaking
|
||||
- [JsonTypeBuilder and JavaScriptTypeBuilder](#JsonTypeBuilder-and-JavaScriptTypeBuilder)
|
||||
- [TypeSystemPolicy](#TypeSystemPolicy)
|
||||
|
||||
<a name="Transform-Types"></a>
|
||||
|
||||
## Transform Types
|
||||
|
||||
Revision 0.31.0 includes a new codec system referred to as Transform types. A Transform type is used to augment a regular TypeBox type with Encode and Decode functions. These functions are invoked via the new Encode and Decode functions available on both Value and TypeCompiler modules.
|
||||
|
||||
The following shows a Transform type which increments and decrements a number.
|
||||
|
||||
```typescript
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
|
||||
const T = Type.Transform(Type.Number()) // const T = {
|
||||
.Decode(value => value + 1) // type: 'number',
|
||||
.Encode(value => value - 1) // [Symbol(TypeBox.Kind)]: 'Number',
|
||||
// [Symbol(TypeBox.Transform)]: {
|
||||
// Decode: [Function: Decode],
|
||||
// Encode: [Function: Encode]
|
||||
// }
|
||||
// }
|
||||
|
||||
const A = Value.Decode(T, 0) // const A: number = 1
|
||||
|
||||
const B = Value.Encode(T, 1) // const B: number = 0
|
||||
```
|
||||
|
||||
<a name="Encode-and-Decode"></a>
|
||||
|
||||
## Encode and Decode
|
||||
|
||||
Revision 0.31.0 includes new functions to Decode and Encode values. These functions are written in service to Transform types, but can be used equally well without them. These functions return a typed value that matches the type being transformed. TypeBox will infer decode and encode differently, yielding the correct type as derived from the codec implementation.
|
||||
|
||||
The following shows decoding and encoding between number to Date. Note these functions will throw if the value is invalid.
|
||||
|
||||
```typescript
|
||||
const T = Type.Transform(Type.Number())
|
||||
.Decode(value => new Date(value)) // number to Date
|
||||
.Encode(value => value.getTime()) // Date to number
|
||||
|
||||
// Ok
|
||||
//
|
||||
const A = Value.Decode(T, 42) // const A = new Date(42)
|
||||
|
||||
const B = Value.Encode(T, new Date(42)) // const B = 42
|
||||
|
||||
// Error
|
||||
//
|
||||
const C = Value.Decode(T, true) // Error: Expected number
|
||||
|
||||
const D = Value.Encode(T, 'not a date') // Error: getTime is not a function
|
||||
```
|
||||
The Decode function is extremely fast when decoding regular TypeBox types; and TypeBox will by pass codec execution if the type being decoded contains no interior Transforms (and will only use Check). When using Transforms however, these functions may incur a performance penelty due to codecs operating structurally on values using dynamic techniques (as would be the case for applications manually decoding values). As such the Decode design is built to be general and opt in, but not necessarily high performance.
|
||||
|
||||
<a name="StaticEncode-and-StaticDecode"></a>
|
||||
|
||||
## StaticEncode and StaticDecode
|
||||
|
||||
Revision 0.31.0 includes new inference types `StaticEncode` and `StaticDecode`. These types can be used to infer the encoded and decoded states of a Transform as well as regular TypeBox types. These types can be used to replace `Static` for `Request` and `Response` inference pipelines.
|
||||
|
||||
The following shows an example `Route` function that uses Transform inference via `StaticDecode`.
|
||||
|
||||
```typescript
|
||||
// Route
|
||||
//
|
||||
export type RouteCallback<TRequest extends TSchema, TResponse extends TSchema> =
|
||||
(request: StaticDecode<TRequest>) => StaticDecode<TResponse> // replace Static with StaticDecode
|
||||
|
||||
export function Route<TPath extends string, TRequest extends TSchema, TResponse extends TSchema>(
|
||||
path: TPath,
|
||||
requestType: TRequest,
|
||||
responseType: TResponse,
|
||||
callback: RouteCallback<TRequest, TResponse>
|
||||
) {
|
||||
// route handling here ...
|
||||
|
||||
const input = null // receive input
|
||||
const request = Value.Decode(requestType, input)
|
||||
const response = callback(request)
|
||||
const output = Value.Encode(responseType, response)
|
||||
// send output
|
||||
}
|
||||
|
||||
// Route: Without Transform
|
||||
//
|
||||
const Timestamp = Type.Number()
|
||||
|
||||
Route('/exampleA', Timestamp, Timestamp, (value) => {
|
||||
return value // value observed as number
|
||||
})
|
||||
|
||||
// Route: With Transform
|
||||
//
|
||||
const Timestamp = Type.Transform(Type.Number())
|
||||
.Decode(value => new Date(value))
|
||||
.Encode(value => value.getTime())
|
||||
|
||||
Route('/exampleB', Timestamp, Timestamp, (value) => {
|
||||
return value // value observed as Date
|
||||
})
|
||||
```
|
||||
|
||||
<a name="Rest-Types"></a>
|
||||
|
||||
## Rest Types
|
||||
|
||||
Revision 0.31.0 updates the Rest type to support variadic tuple extraction from Union, Intersection and Tuple types. Previously the Rest type was limited to Tuple types only, but has been extended to other types to allow uniform remapping without having to extract types from specific schema representations.
|
||||
|
||||
The following remaps a Tuple into a Union.
|
||||
|
||||
```typescript
|
||||
const T = Type.Tuple([ // const T = {
|
||||
Type.String(), // type: 'array',
|
||||
Type.Number() // items: [
|
||||
]) // { type: 'string' },
|
||||
// { type: 'number' }
|
||||
// ],
|
||||
// additionalItems: false,
|
||||
// minItems: 2,
|
||||
// maxItems: 2,
|
||||
// }
|
||||
|
||||
const R = Type.Rest(T) // const R = [
|
||||
// { type: 'string' },
|
||||
// { type: 'number' }
|
||||
// ]
|
||||
|
||||
const U = Type.Union(R) // const U = {
|
||||
// anyOf: [
|
||||
// { type: 'string' },
|
||||
// { type: 'number' }
|
||||
// ]
|
||||
// }
|
||||
```
|
||||
This type can be used to remap Intersect a Composite
|
||||
|
||||
```typescript
|
||||
const I = Type.Intersect([ // const I = {
|
||||
Type.Object({ x: Type.Number() }), // allOf: [{
|
||||
Type.Object({ y: Type.Number() }) // type: 'object',
|
||||
]) // required: ['x'],
|
||||
// properties: {
|
||||
// x: { type: 'number' }
|
||||
// }
|
||||
// }, {
|
||||
// type: 'object',
|
||||
// required: ['y'],
|
||||
// properties: {
|
||||
// y: { type: 'number' }
|
||||
// }
|
||||
// }]
|
||||
// }
|
||||
|
||||
const C = Type.Composite(Type.Rest(I)) // const C = {
|
||||
// type: 'object',
|
||||
// required: ['x', 'y'],
|
||||
// properties: {
|
||||
// 'x': { type: 'number' },
|
||||
// 'y': { type: 'number' }
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Record-Key"></a>
|
||||
|
||||
## Record Key
|
||||
|
||||
Revision 0.31.0 updates the inference strategy for Record types and generalizes RecordKey to TSchema. This update aims to help Record types compose better when used with generic functions. The update also removes the overloaded Record factory methods, opting for a full conditional inference path. It also removes the `RecordKey` type which would type error when used with Record overloads. The return type of Record will be TNever if passing an invalid key. Valid Record key types include TNumber, TString, TInteger, TTemplateLiteral, TLiteralString, TLiteralNumber and TUnion.
|
||||
|
||||
```typescript
|
||||
// 0.30.0
|
||||
//
|
||||
import { RecordKey, TSchema } from '@sinclair/typebox'
|
||||
|
||||
function StrictRecord<K extends RecordKey, T extends TSchema>(K: K, T: T) {
|
||||
return Type.Record(K, T, { additionalProperties: false }) // Error: RecordKey unresolvable to overload
|
||||
}
|
||||
// 0.31.0
|
||||
//
|
||||
import { TSchema } from '@sinclair/typebox'
|
||||
|
||||
function StrictRecord<K extends TSchema, T extends TSchema>(K: K, T: T) {
|
||||
return Type.Record(K, T, { additionalProperties: false }) // Ok: dynamically mapped
|
||||
}
|
||||
|
||||
const A = StrictRecord(Type.String(), Type.Null()) // const A: TRecord<TString, TNull>
|
||||
|
||||
const B = StrictRecord(Type.Literal('A'), Type.Null()) // const B: TObject<{ A: TNull }>
|
||||
|
||||
const C = StrictRecord(Type.BigInt(), Type.Null()) // const C: TNever
|
||||
```
|
||||
|
||||
<a name="TypeBoxError"></a>
|
||||
|
||||
## TypeBoxError
|
||||
|
||||
Revision 0.31.0 updates all errors thrown by TypeBox to extend the sub type `TypeBoxError`. This can be used to help narrow down the source of errors in `try/catch` blocks.
|
||||
|
||||
```typescript
|
||||
import { Type, TypeBoxError } from '@sinclair/typebox'
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
|
||||
try {
|
||||
const A = Value.Decode(Type.Number(), 'hello')
|
||||
} catch(error) {
|
||||
if(error instanceof TypeBoxError) {
|
||||
// typebox threw this error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="TypeSystemErrorFunction"></a>
|
||||
|
||||
## TypeSystemErrorFunction
|
||||
|
||||
Revision 0.31.0 adds functionality to remap error messages with the TypeSystemErrorFunction. This function is invoked whenever a validation error is generated in TypeBox. The following is an example of a custom TypeSystemErrorFunction using some of the messages TypeBox generates by default. TypeBox also provides the DefaultErrorFunction which can be used for fallthrough cases.
|
||||
|
||||
```typescript
|
||||
import { TypeSystemErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/system'
|
||||
|
||||
// Example CustomErrorFunction
|
||||
export function CustomErrorFunction(schema: Types.TSchema, errorType: ValueErrorType) {
|
||||
switch (errorType) {
|
||||
case ValueErrorType.ArrayContains:
|
||||
return 'Expected array to contain at least one matching value'
|
||||
case ValueErrorType.ArrayMaxContains:
|
||||
return `Expected array to contain no more than ${schema.maxContains} matching values`
|
||||
case ValueErrorType.ArrayMinContains:
|
||||
return `Expected array to contain at least ${schema.minContains} matching values`
|
||||
...
|
||||
default: return DefaultErrorFunction(schema, errorType)
|
||||
}
|
||||
}
|
||||
// Sets the CustomErrorFunction
|
||||
TypeSystemErrorFunction.Set(CustomErrorFunction)
|
||||
```
|
||||
It is possible to call `.Set()` on the TypeSystemErrorFunction module prior to each call to `.Errors()`. This can be useful for applications that require i18n support in their validation pipelines.
|
||||
|
||||
<a name="Reduce Package Size"></a>
|
||||
|
||||
## Reduce Package Size
|
||||
|
||||
Revision 0.31.0 completes a full sweep of code optimizations and modularization to reduce package bundle size. The following table shows the bundle sizes inclusive of the new 0.31.0 functionality against 0.30.0.
|
||||
|
||||
```typescript
|
||||
// Revision 0.30.0
|
||||
//
|
||||
┌──────────────────────┬────────────┬────────────┬─────────────┐
|
||||
│ (index) │ Compiled │ Minified │ Compression │
|
||||
├──────────────────────┼────────────┼────────────┼─────────────┤
|
||||
│ typebox/compiler │ '131.4 kb' │ ' 59.4 kb' │ '2.21 x' │
|
||||
│ typebox/errors │ '113.6 kb' │ ' 50.9 kb' │ '2.23 x' │
|
||||
│ typebox/system │ ' 78.5 kb' │ ' 32.5 kb' │ '2.42 x' │
|
||||
│ typebox/value │ '182.8 kb' │ ' 80.0 kb' │ '2.28 x' │
|
||||
│ typebox │ ' 77.4 kb' │ ' 32.0 kb' │ '2.42 x' │
|
||||
└──────────────────────┴────────────┴────────────┴─────────────┘
|
||||
|
||||
// Revision 0.31.0
|
||||
//
|
||||
┌──────────────────────┬────────────┬────────────┬─────────────┐
|
||||
│ (index) │ Compiled │ Minified │ Compression │
|
||||
├──────────────────────┼────────────┼────────────┼─────────────┤
|
||||
│ typebox/compiler │ '149.5 kb' │ ' 66.1 kb' │ '2.26 x' │
|
||||
│ typebox/errors │ '112.1 kb' │ ' 49.4 kb' │ '2.27 x' │
|
||||
│ typebox/system │ ' 83.2 kb' │ ' 37.1 kb' │ '2.24 x' │
|
||||
│ typebox/value │ '191.1 kb' │ ' 82.7 kb' │ '2.31 x' │
|
||||
│ typebox │ ' 73.0 kb' │ ' 31.9 kb' │ '2.29 x' │
|
||||
└──────────────────────┴────────────┴────────────┴─────────────┘
|
||||
```
|
||||
|
||||
Additional code reductions may not be possible without implicating code maintainability. The `typebox` module may however be broken down into sub modules in later revisions to further bolster modularity, but is retained as a single file on this revision for historical reasons (not necessarily technical ones).
|
||||
|
||||
<a name="JsonTypeBuilder-and-JavaScriptTypeBuilder"></a>
|
||||
|
||||
## JsonTypeBuilder and JavaScriptTypeBuilder
|
||||
|
||||
Revision 0.31.0 renames the `StandardTypeBuilder` and `ExtendedTypeBuilder` to `JsonTypeBuilder` and `JavaScriptTypeBuilder` respectively. Applications that extend TypeBox's TypeBuilders will need to update to these names.
|
||||
|
||||
```typescript
|
||||
// 0.30.0
|
||||
//
|
||||
export class ApplicationTypeBuilder extends ExtendedTypeBuilder {}
|
||||
|
||||
// 0.31.0
|
||||
//
|
||||
export class ApplicationTypeBuilder extends JavaScriptTypeBuilder {}
|
||||
```
|
||||
These builders also update the jsdoc comment to `[Json]` and `[JavaScript]` inline with this new naming convention.
|
||||
|
||||
<a name="TypeSystemPolicy"></a>
|
||||
|
||||
## TypeSystemPolicy
|
||||
|
||||
Revision 0.31.0 moves the `TypeSystem.Policy` configurations into a new type named `TypeSystemPolicy`. This change was done to unify internal policy checks used by the Value and Error modules during bundle size optimization; as well as to keep policy configurations contextually separate from the Type and Format API on the TypeSystem module.
|
||||
|
||||
```typescript
|
||||
// Revision 0.30.0
|
||||
//
|
||||
import { TypeSystem } from '@sinclair/typebox/system'
|
||||
|
||||
TypeSystem.AllowNaN = true
|
||||
|
||||
// Revision 0.31.0
|
||||
//
|
||||
import { TypeSystemPolicy } from '@sinclair/typebox/system'
|
||||
|
||||
TypeSystemPolicy.AllowNaN = true
|
||||
|
||||
TypeSystemPolicy.IsNumberLike(NaN) // true
|
||||
```
|
||||
514
changelog/0.32.0.md
Normal file
514
changelog/0.32.0.md
Normal file
@@ -0,0 +1,514 @@
|
||||
### 0.32.0
|
||||
|
||||
---
|
||||
|
||||
### Revision Updates
|
||||
|
||||
- [Revision 0.32.35](https://github.com/sinclairzx81/typebox/pull/914) Support Any for Record keys, Revert error message on required property, Fix order dependency for Union Convert.
|
||||
- [Revision 0.32.34](https://github.com/sinclairzx81/typebox/pull/914) Fix template literal generation for template literals embedded within template literals.
|
||||
- [Revision 0.32.33](https://github.com/sinclairzx81/typebox/pull/905) Pin ESM compiler target to ES2020.
|
||||
- [Revision 0.32.32](https://github.com/sinclairzx81/typebox/pull/898) Fix for Enum properties when used with Mapped types.
|
||||
- [Revision 0.32.31](https://github.com/sinclairzx81/typebox/pull/881) Fix for Cast. Dereference Union variants before scoring.
|
||||
- [Revision 0.32.30](https://github.com/sinclairzx81/typebox/pull/868) Support null object prototypes for Encode/Decode.
|
||||
- [Revision 0.32.29](https://github.com/sinclairzx81/typebox/pull/862) Key derive optimization to improve Intersect Encode/Decode performance.
|
||||
- [Revision 0.32.28](https://github.com/sinclairzx81/typebox/pull/861) Fix for TransformEncode introduced with 0.32.24, 0.32.25 optimizations.
|
||||
- [Revision 0.32.27](https://github.com/sinclairzx81/typebox/pull/854) Support for esm.sh and general build tooling updates.
|
||||
- [Revision 0.32.26](https://github.com/sinclairzx81/typebox/pull/851) Optimization for number checks, use Number.isFinite(x) over typeof `number`.
|
||||
- [Revision 0.32.25](https://github.com/sinclairzx81/typebox/pull/849) Optimizations for type builder to improve schema creation performance for computed types.
|
||||
- [Revision 0.32.24](https://github.com/sinclairzx81/typebox/pull/848) Optimizations for Convert to avoid unnecessary object initialization and cloning.
|
||||
- [Revision 0.32.22](https://github.com/sinclairzx81/typebox/pull/840) Add Support for Optional and Readonly Function and Constructor Arguments.
|
||||
- [Revision 0.32.21](https://github.com/sinclairzx81/typebox/pull/836) Refactor Array Conversion logic. Discard TNever on TComposite.
|
||||
- [Revision 0.32.20](https://github.com/sinclairzx81/typebox/pull/810) Fix compiler regression (TS 5.3.3 -> 5.4.2) generating Diff declaration structures.
|
||||
- [Revision 0.32.19](https://github.com/sinclairzx81/typebox/pull/805) Revert Union Convert logic added on 0.32.16.
|
||||
- [Revision 0.32.18](https://github.com/sinclairzx81/typebox/pull/801) Add explicit return type on TypeSystem.Type.
|
||||
- [Revision 0.32.17](https://github.com/sinclairzx81/typebox/pull/799) Detect ambiguous inference for StaticDecode when inferring as any.
|
||||
- [Revision 0.32.16](https://github.com/sinclairzx81/typebox/pull/791) Enhance Composite, Mapped, Indexed and Transform types. Intersect and Union Convert updates, Include Path in Validation Error.
|
||||
- [Revision 0.32.15](https://github.com/sinclairzx81/typebox/pull/774) Additional internal guards for Type Arrays, Map and Set structures.
|
||||
- [Revision 0.32.14](https://github.com/sinclairzx81/typebox/pull/753) Use barrel exports for submodules.
|
||||
- [Revision 0.32.13](https://github.com/sinclairzx81/typebox/pull/744) Add minLength and maxLength constraint for RegExp
|
||||
- [Revision 0.32.12](https://github.com/sinclairzx81/typebox/pull/740) Fix option assignment on Record types.
|
||||
- [Revision 0.32.11](https://github.com/sinclairzx81/typebox/pull/738) Optimize Extract, Exclude. Overloads for Template Literal
|
||||
- [Revision 0.32.10](https://github.com/sinclairzx81/typebox/pull/734) Export additional type infrastructure for Partial and Required
|
||||
- [Revision 0.32.9](https://github.com/sinclairzx81/typebox/pull/731) Generalize Composite to accept schematics of type TSchema[]
|
||||
- [Revision 0.32.8](https://github.com/sinclairzx81/typebox/pull/728) Ensure schema `default` annotation is cloned on Create.
|
||||
- [Revision 0.32.7](https://github.com/sinclairzx81/typebox/pull/727) Ensure schema `default` annotation is cloned on Default.
|
||||
- [Revision 0.32.6](https://github.com/sinclairzx81/typebox/pull/724) Export additional type infrastructure for mapping types
|
||||
- [Revision 0.32.5](https://github.com/sinclairzx81/typebox/pull/718) Update licence year span for 2024
|
||||
- [Revision 0.32.4](https://github.com/sinclairzx81/typebox/pull/708) Ensure ErrorFunctionParameter type is exported
|
||||
- [Revision 0.32.3](https://github.com/sinclairzx81/typebox/pull/703) Simplify Record Static Type
|
||||
- [Revision 0.32.1](https://github.com/sinclairzx81/typebox/pull/701) Specify default exports for Web Pack
|
||||
|
||||
|
||||
## [0.32.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.32.0)
|
||||
|
||||
## Overview
|
||||
|
||||
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.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Type Imports](#Type-Imports)
|
||||
- [Value Function Import](#Value-Function-Imports)
|
||||
- [CommonJS and ESM](#CommonJS-and-ESM)
|
||||
- [Types](#Types)
|
||||
- [Mapped Type](#Types-Mapped-Type)
|
||||
- [Const Type](#Types-Const-Type)
|
||||
- [Deref Type](#Types-Deref-Type)
|
||||
- [RegExp Type](#Types-RegExp-Type)
|
||||
- [Subtract Modifiers](#Types-Subtract-Modifiers)
|
||||
- [Values](#Values)
|
||||
- [Clean Function](#Values-Clean-Function)
|
||||
- [Default Function](#Values-Default-Function)
|
||||
- [Errors](#Errors)
|
||||
- [Error Parameter](#Errors-Error-Parameter)
|
||||
- [Optimizations](#Optimizations)
|
||||
- [Bundle Size](#Optimizations-Bundle-Size)
|
||||
- [Breaking](#Breaking)
|
||||
- [Renamed Symbols](#Breaking-Renamed-Symbols)
|
||||
- [TypeGuard Interface Change](#Breaking-TypeGuard-Interface-Change)
|
||||
- [Value Submodule Imports](#Breaking-Value-Submodule-Imports)
|
||||
- [Error Function](#Breaking-Error-Function)
|
||||
- [RegEx](#Breaking-RegEx)
|
||||
|
||||
<a name="Type-Imports"></a>
|
||||
|
||||
### Type Imports
|
||||
|
||||
Revision 0.32.0 adds the ability to import types individually.
|
||||
|
||||
```typescript
|
||||
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
|
||||
```
|
||||
|
||||
<a name="Value-Function-Imports"></a>
|
||||
|
||||
### Value Function Imports
|
||||
|
||||
Revision 0.32.0 adds the ability to import value functions from the `/value` module path.
|
||||
|
||||
```typescript
|
||||
import { Value } from '@sinclair/typebox/value' // classic - 61.5 kb minified
|
||||
|
||||
import { Check } from '@sinclair/typebox/value' // selective - 18.2 kb minified
|
||||
```
|
||||
|
||||
### CommonJS and ESM
|
||||
|
||||
<a name="CommonJS-and-ESM"></a>
|
||||
|
||||
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.
|
||||
|
||||
<a name="Types"></a>
|
||||
|
||||
## Types
|
||||
|
||||
Revision 0.32.0 adds three new types to the type system and makes enhancements to Readonly and Optional modifiers.
|
||||
|
||||
<a name="Types-Mapped-Type"></a>
|
||||
|
||||
### Mapped Type
|
||||
|
||||
Revision 0.32.0 adds the Mapped type which replicates TS [Mapped Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) at runtime. The following shows the syntax comparison between TypeScript and TypeBox.
|
||||
|
||||
#### TypeScript
|
||||
|
||||
```typescript
|
||||
type T = {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number
|
||||
}
|
||||
|
||||
type M = { [K in keyof T]: T[K] } // a mapped type
|
||||
```
|
||||
#### TypeBox
|
||||
```typescript
|
||||
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](https://sinclairzx81.github.io/typebox-workbench/) which can generate runtime Mapped types from TypeScript syntax.
|
||||
|
||||
<a name="Types-Const-Type"></a>
|
||||
|
||||
### Const Type
|
||||
|
||||
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.
|
||||
|
||||
```typescript
|
||||
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](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#const-type-parameters). This update will enable TypeBox to correctly infer the readonly literal type without the need for `as const`.
|
||||
|
||||
|
||||
<a name="Types-Deref-Type"></a>
|
||||
|
||||
### Deref Type
|
||||
|
||||
Revision 0.32.0 adds a new Type.Deref type which can be used to dereference type schematics.
|
||||
|
||||
```typescript
|
||||
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.
|
||||
|
||||
|
||||
<a name="Types-RegExp-Type"></a>
|
||||
|
||||
### RegExp Type
|
||||
|
||||
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](https://json-schema.org/understanding-json-schema/reference/regular_expressions) 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.
|
||||
|
||||
```typescript
|
||||
// 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.
|
||||
|
||||
```typescript
|
||||
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
|
||||
```
|
||||
|
||||
<a name="Types-Subtractive-Modifiers"></a>
|
||||
|
||||
### Subtract Modifier
|
||||
|
||||
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.
|
||||
|
||||
#### TypeScript
|
||||
```typescript
|
||||
type T = {
|
||||
x?: number,
|
||||
y?: number
|
||||
}
|
||||
type M = { [K in keyof T]-?: T[K] } // -? - subtract optional modifier
|
||||
```
|
||||
#### TypeBox
|
||||
```typescript
|
||||
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.
|
||||
|
||||
<a name="Values"></a>
|
||||
|
||||
## Values
|
||||
|
||||
Revision 0.32.0 adds two new functions to the Value module.
|
||||
|
||||
<a name="Values-Clean-Function"></a>
|
||||
|
||||
### Clean Function
|
||||
|
||||
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.
|
||||
|
||||
```typescript
|
||||
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.
|
||||
|
||||
<a name="Values-Default-Function"></a>
|
||||
|
||||
### Default Function
|
||||
|
||||
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.
|
||||
|
||||
```typescript
|
||||
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.
|
||||
|
||||
<a name="Optimizations"></a>
|
||||
|
||||
## Optimizations
|
||||
|
||||
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.
|
||||
|
||||
<a name="Optimizations-Bundle-Size"></a>
|
||||
|
||||
```typescript
|
||||
// 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' │
|
||||
└──────────────────────┴────────────┴────────────┴─────────────┘
|
||||
```
|
||||
<a name="Errors"></a>
|
||||
|
||||
## Errors
|
||||
|
||||
Revision 0.32.0 makes some enhancements to errors.
|
||||
|
||||
<a name="Errors-Error-Parameter"></a>
|
||||
|
||||
### Error Parameter
|
||||
|
||||
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.
|
||||
|
||||
```typescript
|
||||
// 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.
|
||||
|
||||
<a name="Breaking"></a>
|
||||
|
||||
## Breaking
|
||||
|
||||
The following list the breaking changes in Revision 0.32.0.
|
||||
|
||||
|
||||
<a name="Breaking-Renamed-Symbols"></a>
|
||||
|
||||
### Renamed Symbols
|
||||
|
||||
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.
|
||||
|
||||
```typescript
|
||||
// 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'
|
||||
```
|
||||
|
||||
<a name="Breaking-TypeGuard-Interface-Change"></a>
|
||||
|
||||
### TypeGuard Interface Change
|
||||
|
||||
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.
|
||||
|
||||
```typescript
|
||||
// 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({ ... })
|
||||
```
|
||||
|
||||
<a name="Breaking-Value-Submodule-Imports"></a>
|
||||
|
||||
### Value Submodule Imports
|
||||
|
||||
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)
|
||||
|
||||
```typescript
|
||||
// Revision 0.31.0
|
||||
|
||||
import { Check } from '@sinclair/typebox/value/check'
|
||||
|
||||
// Revision 0.32.0
|
||||
|
||||
import { Check } from '@sinclair/typebox/value'
|
||||
```
|
||||
|
||||
<a name="Breaking-Error-Function"></a>
|
||||
|
||||
### Error Function
|
||||
|
||||
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)
|
||||
|
||||
```typescript
|
||||
// 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)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
<a name="Breaking-RegEx"></a>
|
||||
|
||||
### RegEx
|
||||
|
||||
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.
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
```
|
||||
59
changelog/0.33.0.md
Normal file
59
changelog/0.33.0.md
Normal file
@@ -0,0 +1,59 @@
|
||||
### 0.33.0
|
||||
|
||||
---
|
||||
|
||||
### Revision Updates
|
||||
|
||||
- [Revision 0.33.22](https://github.com/sinclairzx81/typebox/pull/1065)
|
||||
- Rename TypeScript parsing infrastructure from `/parse` to `/syntax`. Remove Parse API from top level import.
|
||||
- [Revision 0.33.21](https://github.com/sinclairzx81/typebox/pull/1064)
|
||||
- [1063](https://github.com/sinclairzx81/typebox/issues/1063) Hotfix to resolve variable shadowing on Object (Parser Runtime)
|
||||
- [Revision 0.33.20](https://github.com/sinclairzx81/typebox/pull/1062)
|
||||
- Add TypeScript Parsing Infrastructure. Add Parse API to top level import.
|
||||
- [Revision 0.33.19](https://github.com/sinclairzx81/typebox/pull/1061)
|
||||
- Preemptive fix for TypeScript 5.8.0-dev (Type Fix for Immutable Function)
|
||||
- [Revision 0.33.18](https://github.com/sinclairzx81/typebox/pull/1060)
|
||||
- [1052](https://github.com/sinclairzx81/typebox/pull/1052) Export the Encode | Decode functions directly. Refactoring on Value submodule.
|
||||
- [1057](https://github.com/sinclairzx81/typebox/pull/1057) Export Object with var declaration to prevent global shadowing. Related Babel [Issue](https://github.com/babel/babel/issues/16943).
|
||||
- [Revision 0.33.17](https://github.com/sinclairzx81/typebox/pull/1042)
|
||||
- [1041](https://github.com/sinclairzx81/typebox/issues/1041) Avoid Exponentiation operator on Value.Hash
|
||||
- [Revision 0.33.16](https://github.com/sinclairzx81/typebox/pull/1015)
|
||||
- [1015](https://github.com/sinclairzx81/typebox/issues/1015) Add sub error iterators to ValueError
|
||||
- [Revision 0.33.15](https://github.com/sinclairzx81/typebox/pull/1025)
|
||||
- [1024](https://github.com/sinclairzx81/typebox/issues/1024) Fix to correctly resolve default Dates
|
||||
- [Revision 0.33.14](https://github.com/sinclairzx81/typebox/pull/1019)
|
||||
- [1019](https://github.com/sinclairzx81/typebox/pull/1019) Converting Large Numbers to BigInt
|
||||
- [Revision 0.33.13](https://github.com/sinclairzx81/typebox/pull/1011)
|
||||
- [1010](https://github.com/sinclairzx81/typebox/pull/1011) Fixes Value.Parse fails with recursive types
|
||||
- [Revision 0.33.12](https://github.com/sinclairzx81/typebox/pull/999)
|
||||
- [998](https://github.com/sinclairzx81/typebox/issues/998) Avoid losing precision when converting to bigints
|
||||
- [Revision 0.33.11](https://github.com/sinclairzx81/typebox/pull/994)
|
||||
- [993](https://github.com/sinclairzx81/typebox/issues/993) Prevent mutation on union values during Convert
|
||||
- [Revision 0.33.10](https://github.com/sinclairzx81/typebox/pull/991)
|
||||
- [907](https://github.com/sinclairzx81/typebox/issues/907) Add package.json metadata to specify possible side effect modules
|
||||
- [Revision 0.33.9](https://github.com/sinclairzx81/typebox/pull/984)
|
||||
- [887](https://github.com/sinclairzx81/typebox/issues/887) Generate Nested Intersect Errors
|
||||
- [Revision 0.33.8](https://github.com/sinclairzx81/typebox/pull/983)
|
||||
- [982](https://github.com/sinclairzx81/typebox/issues/982) Prevent Intersect Transform Encode callback from being called twice
|
||||
- [974](https://github.com/sinclairzx81/typebox/issues/974) Make strict the Encode and Decode return type
|
||||
- [975](https://github.com/sinclairzx81/typebox/issues/975) Support default annotation being assigned Functions for lazy value initialization on Create
|
||||
- [980](https://github.com/sinclairzx81/typebox/issues/980) Enable Mapping types to override user defined options from source type
|
||||
- [976](https://github.com/sinclairzx81/typebox/issues/976) Support Constraint Copy for Pick, Omit (inline with Partial / Required) (Trialing Implementation)
|
||||
- Flag Strict For Deprecation
|
||||
- [Revision 0.33.7](https://github.com/sinclairzx81/typebox/pull/964)
|
||||
- Additional updates to improve Default for enumerable objects.
|
||||
- [Revision 0.33.6](https://github.com/sinclairzx81/typebox/pull/963)
|
||||
- Add object traversal path for Default. Ensure enumerable objects are traversed.
|
||||
- [Revision 0.33.5](https://github.com/sinclairzx81/typebox/pull/959)
|
||||
- Provide better support for transforming properties with optional modifiers.
|
||||
- [Revision 0.33.4](https://github.com/sinclairzx81/typebox/pull/953)
|
||||
- Add Assert and Parse value functions. Add defacto AssertError type.
|
||||
- [Revision 0.33.3](https://github.com/sinclairzx81/typebox/pull/950)
|
||||
- Optimize Value Diff algorithm. Update edit sequence to INSERT, UPDATE then DELETE.
|
||||
- [Revision 0.33.2](https://github.com/sinclairzx81/typebox/pull/947)
|
||||
- Ensure user defined schema options are retained on mapping types, Pick, Omit and Mapped.
|
||||
- [Revision 0.33.1](https://github.com/sinclairzx81/typebox/pull/945)
|
||||
- Apply mutability fix for Intrinsic and Not schematics (inline with Default)
|
||||
- [Revision 0.33.0](https://github.com/sinclairzx81/typebox/pull/941)
|
||||
- Add InstanceMode to enable Clone, Freeze and Default schema initialization options. Optimize for Default.
|
||||
|
||||
289
changelog/0.34.0.md
Normal file
289
changelog/0.34.0.md
Normal file
@@ -0,0 +1,289 @@
|
||||
### 0.34.0
|
||||
|
||||
---
|
||||
|
||||
### Revision Updates
|
||||
- [Revision 0.34.41](https://github.com/sinclairzx81/typebox/pull/1310)
|
||||
- Disable Node10 Module Resolution | TS7 Deprecation Warning.
|
||||
- [Revision 0.34.40](https://github.com/sinclairzx81/typebox/pull/1293)
|
||||
- Use Uniform over Reciprocal weighting on Cast Union Select
|
||||
- [Revision 0.34.39](https://github.com/sinclairzx81/typebox/pull/1296)
|
||||
- Guard for Array in Object and Record conversion
|
||||
- [Revision 0.34.38](https://github.com/sinclairzx81/typebox/pull/1282)
|
||||
- Preserve exact type matches in Union conversion
|
||||
- [Revision 0.34.37](https://github.com/sinclairzx81/typebox/pull/1278)
|
||||
- Fix Support nested Union selection when scoring for Cast
|
||||
- [Revision 0.34.36](https://github.com/sinclairzx81/typebox/pull/1276)
|
||||
- Fix Record Intersect on Cast
|
||||
- [Revision 0.34.35](https://github.com/sinclairzx81/typebox/pull/1265)
|
||||
- Deep Assign on Intersect Cast
|
||||
- [Revision 0.34.34](https://github.com/sinclairzx81/typebox/pull/1263)
|
||||
- Support Inference of Ref inside Recursive inside Module
|
||||
- [Revision 0.34.33](https://github.com/sinclairzx81/typebox/pull/1220)
|
||||
- Hotfix: Correct Invalid Import Specifier
|
||||
- [Revision 0.34.32](https://github.com/sinclairzx81/typebox/pull/1218)
|
||||
- Accelerated | High Performance Syntax Parsing
|
||||
- [Revision 0.34.31](https://github.com/sinclairzx81/typebox/pull/1209)
|
||||
- Use Tail Call Optimized Inference for Records with Large Union Keys
|
||||
- [Revision 0.34.30](https://github.com/sinclairzx81/typebox/pull/1198)
|
||||
- Additional Syntax Parsing Optimizations
|
||||
- [Revision 0.34.29](https://github.com/sinclairzx81/typebox/pull/1197)
|
||||
- Syntax Parsing Optimizations
|
||||
- [Revision 0.34.28](https://github.com/sinclairzx81/typebox/pull/1187)
|
||||
- Add Cast to Configurable Parse Pipeline
|
||||
- [Revision 0.34.27](https://github.com/sinclairzx81/typebox/pull/1182)
|
||||
- [1178](https://github.com/sinclairzx81/typebox/issues/1178) Support Deep Referential Transform Inference Inside Modules
|
||||
- [Revision 0.34.26](https://github.com/sinclairzx81/typebox/pull/1181)
|
||||
- Internal: Use Parser Context Threading for Generic Arguments.
|
||||
- [Revision 0.34.25](https://github.com/sinclairzx81/typebox/pull/1176)
|
||||
- Evaluate Conditional Expression for Instatiated Object Types
|
||||
- [Revision 0.34.24](https://github.com/sinclairzx81/typebox/pull/1175)
|
||||
- Add Support For Generic Parameter Syntax
|
||||
- [Revision 0.34.23](https://github.com/sinclairzx81/typebox/pull/1174)
|
||||
- Inline Instantiate Type Logic Local to Instantiate Module
|
||||
- [Revision 0.34.22](https://github.com/sinclairzx81/typebox/pull/1171)
|
||||
- Intrinsic Types Number, String, Boolean, etc Passthrough on Required and Partial Mapping
|
||||
- [Revision 0.34.21](https://github.com/sinclairzx81/typebox/pull/1168)
|
||||
- Reimplement Computed Record Types
|
||||
- [Revision 0.34.20](https://github.com/sinclairzx81/typebox/pull/1167)
|
||||
- Hotfix: Disable Computed Record Types
|
||||
- [Revision 0.34.19](https://github.com/sinclairzx81/typebox/pull/1166)
|
||||
- Hotfix: Republished due to NPM error
|
||||
- [Revision 0.34.18](https://github.com/sinclairzx81/typebox/pull/1164)
|
||||
- Hotfix: Internal Remap Imports
|
||||
- [Revision 0.34.17](https://github.com/sinclairzx81/typebox/pull/1162)
|
||||
- Add Argument() and Instantiate() Types and Instancing via Syntax support.
|
||||
- [Revision 0.34.16](https://github.com/sinclairzx81/typebox/pull/1156)
|
||||
- Export TypeBox String Parsing Infrastructure
|
||||
- [Revision 0.34.15](https://github.com/sinclairzx81/typebox/pull/1148)
|
||||
- [1147](https://github.com/sinclairzx81/typebox/issues/1147) Fix incorrect truncation for integers that exceed 32-bit values in Value.Convert
|
||||
- [Revision 0.34.14](https://github.com/sinclairzx81/typebox/pull/1140)
|
||||
- [1139](https://github.com/sinclairzx81/typebox/issues/1139) Update TypeCompiler Check for Promise (use instanceof Promise over Thenable check)
|
||||
- [Revision 0.34.13](https://github.com/sinclairzx81/typebox/pull/1124)
|
||||
- Pre emptive fix for TypeScript 5.8.0-nightly to resolve symbol narrowing on Convert.
|
||||
- [Revision 0.34.12](https://github.com/sinclairzx81/typebox/pull/1120)
|
||||
- [1119](https://github.com/sinclairzx81/typebox/issues/1119) Fix for Mutate Object Comparison
|
||||
- [1117](https://github.com/sinclairzx81/typebox/issues/1117) Re-Add Type.Recursive Documentation
|
||||
- [Revision 0.34.11](https://github.com/sinclairzx81/typebox/pull/1110)
|
||||
- Fix Compiler Emit for Deeply Referential Module Types
|
||||
- [Revision 0.34.10](https://github.com/sinclairzx81/typebox/pull/1107)
|
||||
- Fix Declaration Emit for Index and Mapped Types
|
||||
- Fix Record Inference Presentation when Embedded in Modules
|
||||
- Fix Record Mapping when using TImport as Key
|
||||
- Add Encode to Parse Operation List
|
||||
- [Revision 0.34.9](https://github.com/sinclairzx81/typebox/pull/1101)
|
||||
- User Defined Parse Pipelines
|
||||
- Access to Schema and References on TypeCheck
|
||||
- [Revision 0.34.8](https://github.com/sinclairzx81/typebox/pull/1098)
|
||||
- Fix for Computed Readonly and Optional Properties
|
||||
- [Revision 0.34.7](https://github.com/sinclairzx81/typebox/pull/1093)
|
||||
- Revert Ref(Schema) Signature with Deprecation Notice
|
||||
- [Revision 0.34.6](https://github.com/sinclairzx81/typebox/pull/1090)
|
||||
- Add Computed To Type and Kind Guards (IsSchema)
|
||||
- [Revision 0.34.5](https://github.com/sinclairzx81/typebox/pull/1088)
|
||||
- Record Types no longer TCompute for TRef Value Type (Modules)
|
||||
- [Revision 0.34.4](https://github.com/sinclairzx81/typebox/pull/1085)
|
||||
- Inference Path for Enum within Modules
|
||||
- [Revision 0.34.3](https://github.com/sinclairzx81/typebox/pull/1083)
|
||||
- Retain Array Elements on Value Default
|
||||
- [Revision 0.34.2](https://github.com/sinclairzx81/typebox/pull/1082)
|
||||
- Resolve import pathing issue introduced on 0.34.1
|
||||
- [Revision 0.34.1](https://github.com/sinclairzx81/typebox/pull/1080)
|
||||
- Implement Computed Type Deref in Modules
|
||||
|
||||
## [0.34.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.34.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.34.0 represents a significant milestone for the TypeBox project. This update changes how TypeBox manages type references (Ref) and introduces a new Module type to support mutual recursion and self-referencing types. Additionally, it includes a new submodule for parsing TypeScript syntax directly into TypeBox types.
|
||||
|
||||
Please note that this release includes breaking changes to Ref and some deprecations. These updates require a minor semver revision.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Enhancements](#Enhancements)
|
||||
- [Module Types](#Module-Types)
|
||||
- [Syntax Types](#Syntax-Types)
|
||||
- [Breaking Changes](#Breaking-Changes)
|
||||
- [Ref](#Ref)
|
||||
- [Deref](#Deref)
|
||||
- [Strict](#Strict)
|
||||
|
||||
|
||||
<a name="Enhancements"></a>
|
||||
|
||||
## Enhancements
|
||||
|
||||
Below are the enhancements introduced in Version 0.34.0.
|
||||
|
||||
<a name="Module-Type"></a>
|
||||
|
||||
### Module Types
|
||||
|
||||
Revision 0.34.0 introduces a new type, called Module. Modules are represented as JSON Schema $def schematics, specifically designed to support both mutual and self-recursive types. This addition resolves a longstanding issue in TypeBox, where complex recursive structures often encountered "definition order" problems, making certain recursive structures difficult to represent cleanly. With Modules, you can now define schematics within a Module context, allowing them to be referenced within the type system through a separate inference operation.
|
||||
|
||||
```typescript
|
||||
// The following creates a circular recursive type.
|
||||
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B') // Ref B:
|
||||
}),
|
||||
B: Type.Object({
|
||||
c: Type.Ref('C') // Ref C:
|
||||
}),
|
||||
C: Type.Object({
|
||||
a: Type.Ref('A') // Ref A:
|
||||
}),
|
||||
})
|
||||
|
||||
// Module types must be imported before use.
|
||||
|
||||
const A = Module.Import('A') // const A: TImport<{...}, 'A'>
|
||||
|
||||
type A = Static<typeof A> // type A = {
|
||||
// b: {
|
||||
// c: {
|
||||
// a: {
|
||||
// b: ...
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Syntax-Types"></a>
|
||||
|
||||
### Syntax Types
|
||||
|
||||
|
||||
Revision 0.34.0 introduces a new submodule for parsing TypeScript syntax directly into TypeBox types, implemented both at runtime and within the type system. This feature was made possible through the development of a separate project, [ParseBox](https://github.com/sinclairzx81/parsebox) (MIT-licensed), which provides a symmetric runtime and type-level parsing infrastructure.
|
||||
|
||||
As of 0.34.0, Syntax Types are available as an opt-in feature, with the parsing infrastructure adding approximately 10kb (minified) to the existing type builder. With further optimizations, this feature may be elevated to a top-level import in future updates to minimize bundling size.
|
||||
|
||||
To use Syntax Types, import them from the `@sinclair/typebox/syntax` path.
|
||||
|
||||
```typescript
|
||||
import { Parse } from '@sinclair/typebox/syntax'
|
||||
|
||||
// All primitive types are supported
|
||||
|
||||
const A = Parse('string') // const A: TString
|
||||
const B = Parse('number') // const B: TNumber
|
||||
const C = Parse('boolean') // const C: TBoolean
|
||||
|
||||
// ... Multiline parsing is supported (but comments are not)
|
||||
|
||||
const T = Parse(`{
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
}`)
|
||||
|
||||
|
||||
// ... Parametertized parsing is supported
|
||||
const O = Parse({ T }, `T & { w: number }`) // const O: TIntersect<[
|
||||
// TObject<{
|
||||
// x: TNumber,
|
||||
// y: TNumber,
|
||||
// z: TNumber,
|
||||
// }>,
|
||||
// TObject<{
|
||||
// w: TNumber
|
||||
// }>
|
||||
// ]>
|
||||
|
||||
// ... Module parsing is also supported.
|
||||
|
||||
const Math = Parse(`module Math {
|
||||
export interface X {
|
||||
x: number
|
||||
}
|
||||
export interface Y {
|
||||
y: number
|
||||
}
|
||||
export interface Z {
|
||||
z: number
|
||||
}
|
||||
export interface Vector extends X, Y, Z {
|
||||
type: 'Vector'
|
||||
}
|
||||
}`)
|
||||
|
||||
const Vector = Math.Import('Vector')
|
||||
```
|
||||
|
||||
Runtime parsing performance should be quite good; however, static parsing performance could be improved. TypeScript will invoke the parser for each property accessed at design time. Ongoing efforts within the ParseBox project aim to optimize string parsing in TypeScript, with additional research underway into type-level caching strategies within the TypeScript compiler. Additional optimizations will be explored over the course of 0.34.x.
|
||||
|
||||
<a name="Breaking-Changes"></a>
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
The following are the breaking changes in Revision 0.34.0.
|
||||
|
||||
<a name="Ref"></a>
|
||||
|
||||
### Ref
|
||||
|
||||
Revision 0.34.0 introduces a breaking change to Ref, modifying its signature to accept only constant string values. Previously, Ref could accept an existing TypeBox type, provided it had an $id assigned.
|
||||
|
||||
```typescript
|
||||
|
||||
// Revision 0.33.0
|
||||
|
||||
const T = Type.String({ $id: 'T' })
|
||||
|
||||
const R = Type.Ref(T)
|
||||
|
||||
type R = Static<typeof R> // type R = string
|
||||
|
||||
// Revision 0.34.0
|
||||
|
||||
const T = Type.String({ $id: 'T' })
|
||||
|
||||
const R = Type.Ref('T')
|
||||
|
||||
type R = Static<typeof R> // type R = unknown
|
||||
|
||||
```
|
||||
|
||||
In Revision 0.34.0, the inferred type for Ref is now unknown. Implementations using the previous version of Ref can switch to Unsafe to type the reference to the target value.
|
||||
|
||||
```typescript
|
||||
// Revision 0.34.0
|
||||
|
||||
const T = Type.String({ $id: 'T' })
|
||||
|
||||
const R = Type.Unsafe<Static<typeof T>>(Type.Ref('T'))
|
||||
|
||||
type R = Static<typeof R> // type R = string
|
||||
```
|
||||
|
||||
<a name="Deref"></a>
|
||||
|
||||
### Deref
|
||||
|
||||
Revision 0.34.0 removes the Deref type, which was previously used to dereference schematics. Since the Ref signature has changed from TSchema to string, there is no longer a way to resolve reference types accurately. TypeBox may provide a prototype example of this type upon request.
|
||||
|
||||
<a name="Strict"></a>
|
||||
|
||||
### Strict
|
||||
|
||||
Revision 0.34.0 removes the Strict type from the Type Builder, which was deprecated in version 0.33.8. This type was introduced several years ago in response to a change in Ajv that prohibited unknown keywords. At that time, TypeBox used string property keys for `kind` and `modifier`, which required either Ajv configuration or the use of Strict. These properties have since been updated to Symbol properties, resolving the issues with Ajv. However, the Strict type remained due to some use in ecosystem projects, which has since reduced.
|
||||
|
||||
For those who still need Strict, the recommended approach is to use the JSON stringify/parse method outlined in the deprecation notice.
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended
|
||||
* to use the JSON parse/stringify to remove compositing symbols if needed. This
|
||||
* is how Strict works internally.
|
||||
*
|
||||
* ```typescript
|
||||
* JSON.parse(JSON.stringify(Type.String()))
|
||||
* ```
|
||||
*/
|
||||
export function Strict<T extends TSchema>(schema: T): TStrict<T> {
|
||||
return JSON.parse(JSON.stringify(schema))
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user