Publish
This commit is contained in:
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
|
||||
```
|
||||
Reference in New Issue
Block a user