Skip to content

Commit

Permalink
Revision 0.34.0 (#1069)
Browse files Browse the repository at this point in the history
* Add Module Types

* Add Syntax Types
  • Loading branch information
sinclairzx81 authored Nov 13, 2024
1 parent 4e2158d commit 55ae006
Show file tree
Hide file tree
Showing 68 changed files with 2,241 additions and 1,039 deletions.
196 changes: 196 additions & 0 deletions changelog/0.34.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
## [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))
}
```
1 change: 0 additions & 1 deletion example/prototypes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/

export * from './from-schema'
export * from './module'
export * from './partial-deep'
export * from './union-enum'
export * from './union-oneof'
166 changes: 0 additions & 166 deletions example/prototypes/module.ts

This file was deleted.

Loading

0 comments on commit 55ae006

Please sign in to comment.