Revision 0.8.4 (#13)

* Parameterized Types

* Syntax Options

* Documentation
This commit is contained in:
sinclairzx81
2025-01-28 02:39:50 +09:00
committed by GitHub
parent c2991c1972
commit 78dc497ef8
21 changed files with 451 additions and 159 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,10 @@
import { TypeBox, Valibot, Zod } from '@sinclair/typemap'
// TypeScript Syntax Type
// Parse Syntax | Parse Value
const R = Zod('string | number').parse('...') // const R: string | number
// Syntax Type
const S = `{
x: number,
@@ -8,8 +12,6 @@ const S = `{
z: number
}`
// Construct Library Types from Syntax
const T = TypeBox(S) // const T: TObject<{
// x: TNumber,
// y: TNumber,
@@ -29,6 +31,3 @@ const Z = Zod(S) // const Z: ZodObject<{
// z: ZodNumber
// }, ...>
// Parse Syntax | Parse Value
const R = Zod('string | number').parse('...') // const R: string | number

View File

@@ -23,7 +23,7 @@ export async function start() {
// Format
// -------------------------------------------------------------------------------
export async function format() {
await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write test src example/index.ts')
await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write test src')
}
// ------------------------------------------------------------------
// Test

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@sinclair/typemap",
"version": "0.8.3",
"version": "0.8.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@sinclair/typemap",
"version": "0.8.3",
"version": "0.8.4",
"license": "MIT",
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.2",

View File

@@ -1,7 +1,7 @@
{
"name": "@sinclair/typemap",
"version": "0.8.3",
"description": "Uniform Syntax, Mapping and Compiler Library for TypeBox, Valibot and Zod",
"version": "0.8.4",
"description": "Unified Syntax and Type Compiler for Runtime Type Systems",
"author": "sinclairzx81",
"license": "MIT",
"repository": {

233
readme.md
View File

@@ -2,9 +2,9 @@
<h1>TypeMap</h1>
<p>Uniform Syntax, Mapping and Compiler Library for TypeBox, Valibot and Zod</p>
<p>Unified Syntax and Type Compiler for Runtime Type Systems</p>
<img src="https://github.com/sinclairzx81/typemap/blob/main/typemap.png?raw=true" />
<img src="./typemap.png" />
<br />
<br />
@@ -22,16 +22,62 @@
$ npm install @sinclair/typemap --save
```
## Usage
Create and Compile Runtime Types using TypeScript syntax.
```typescript
import { Zod, Compile } from '@sinclair/typemap'
const T = Zod(`string`) // ZodString
const R = Compile(T).Check('Hello from TypeMap')
```
## Overview
TypeMap is a syntax frontend and compiler backend for the [TypeBox](https://github.com/sinclairzx81/typebox), [Valibot](https://github.com/fabian-hiller/valibot) and [Zod](https://github.com/colinhacks/zod) type libraries. It provides a uniform syntax for creating types, a runtime compiler for high-performance validation and enables types written in one library to be transformed to another.
TypeMap is built as an advanced adapter system for the TypeBox project. It enables Valibot and Zod to be integrated on TypeBox validation infrastructure as well as enabling TypeBox to be integrated on Zod infrastructure via reverse type mapping.
License: MIT
## Contents
- [Install](#Install)
- [Usage](#Usage)
- [Overview](#Overview)
- [Example](#Example)
- [Libraries](#Libraries)
- [TypeBox](#TypeBox)
- [Valibot](#Valibot)
- [Zod](#Zod)
- [Syntax](#Syntax)
- [Types](#Types)
- [Options](#Options)
- [Parameters](#Parameters)
- [Generics](#Generics)
- [Static](#Static)
- [Compile](#Compile)
- [Benchmark](#Benchmark)
- [Contribute](#Contribute)
## Example
Use TypeScript syntax to construct types for TypeBox, Valibot and Zod ...
Construct types for TypeBox, Valibot and Zod using a common TypeScript syntax
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgFQJ5gKYCEIA8A0cAagIYA2wARhDIQFoQAmcAvnAGZQQhwDkAAgGdgAOwDGZEsCgB6GOgwgSYXgChVMmSgUBlMVGBh4O1CJglc2zOrEQRg43AC8cAAYJVcOLgBccEQCuIJQYUPiecKh+gcGh4V4AXtFBIVCqLK7qmnAAwnYOUAFi8AAyVFAkUKhWGIIcXDwmZhY2+fDIzjU4uAAUOgCUXkPDI6NjI9m29u1+yADylABWGMUAPB7jm1vbO17ZXr4oAHIpcRG7F5fj+5GzJ7Fh51fPVzdJx6dpL9+X2SwAfK1psROqQKNQYH1Bj9tpM2sQ-AtlsU9AALRQkdZPGE4iZaA5+e6pNEY1ZYEiCDAAUSgXCg-3iuKZwxuUTgRNCJKUZIp1Np0AZ2OZOLehM+XMx5MpNLpgOFwr+hAAdCrAUCHHA6J0GIwofLNnDgXQ-DqkSsYFj9UybocdRzHlbcayTUx7YzHT9RZrXZ8hR7floWMrVX7nhotAAFSqUuBNcyWAA+cCjUBjaEwcHV8AAStqmFClWBoxgerwVUreNCYYaNdm-Bt-d8bckHgBuUON2H427+T7tzsvL0xVL9gevQNAA)
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgFQJ5gKYCEIA8A0cAagIYA2wARhDIQFoQAmcAvnAGZQQhwDkAAgGdgAOwDGZEsCgB6GOgwgSYXgChVMmXAAKJKIIxwAyqhEwSuOAB8deg8XIBXQ+rEQRg+ACU4AXjgMjAAUvJ5QogDm1nAijiCUGFC8AJQAdGB2GCGpOSlw+QVwmnBuHt4AXHBhkdGx8YnqxQDC3CDuxqbmlmiYcF4YYFAYBmYkMMDuru6exn5wAAYIqvm4lXUJUPjLcKhrcRtb+QBee-VQqizzU2Uocz3YeEFGyYWvb+8f+cWlM8iVyAB5SgAKwwYhgAB4lp8YbC4TDiit-gA5faJQ7wzFYz6InYotGbbbY4nE3EnFCos5Ekk0uHFFgAPmuMyIc1IFGoMCeL1psO+03gREqQNB4KMYgAFooSFDqbz5a9cas4JSNuKpUoITlUgyMQr9QVcbsVQT1dKtTldXKDfKyZVVYkzZrtUybTb6YQXepVD94HQ5oFuW6cVpfQFKoERWDIdDgwqlRGmA7CXH41p8sbAsm9amaXaAkmCdbc1iPXAXUA)
```typescript
import { TypeBox, Valibot, Zod } from '@sinclair/typemap'
// TypeScript Syntax Type
// Parse Syntax | Parse Value
const R = Zod('string | number').parse('...') // const R: string | number
// Common Syntax Type Representation
const S = `{
x: number,
@@ -39,8 +85,6 @@ const S = `{
z: number
}`
// Construct Library Types from Syntax
const T = TypeBox(S) // const T: TObject<{
// x: TNumber,
// y: TNumber,
@@ -59,80 +103,55 @@ const Z = Zod(S) // const Z: ZodObject<{
// y: ZodNumber,
// z: ZodNumber
// }, ...>
// Parse Syntax | Parse Value
const R = Zod('string | number').parse('...') // const R: string | number
```
... or structurally remap types from one library to another
Transform types between TypeBox, Valibot and Zod
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgFQJ5gKYCEIA8A0cAagIYA2wARhDIQFoQAmcAvnAGZQQhwDkAAgGdgAOwDGZEsCgB6GOgwgSYXgChVMmXADKqETBK44APjgNmp0hWrxTaTDlzqxEEYPjI4AXhQLHACisqGn9zfwADBFU4OFwALjgRAFcQSgwofGi4VATk1PTMmIAvXJS0qFUWcIBKWqA)
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgFQJ5gKYCEIA8A0cAagIYA2wARhDIQFoQAmcAvnAGZQQhwDkAAgGdgAOwDGZEsCgB6GOgwgSYXgChVYiCMHwAUoK0BlMQAtFJOAF4UCnLgAUpCtRj2GjewAMEquHFwAXHAiAK4glBhQ+L5wqEGh4ZHRfgBe8WERUKosngCU+UA)
```typescript
import { TypeBox, Valibot, Zod } from '@sinclair/typemap'
// Syntax > Zod > Valibot > TypeBox
const T = TypeBox(Valibot(Zod(`{
const JsonSchema = TypeBox(Valibot(Zod(`{
x: number,
y: number,
z: number
}`)))
```
... or compile types for high performance runtime type checking
Compile Zod and Valibot on TypeBox validation infrastructure
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgYQuYAbApnAvnAMyjTgHIABAZ2ADsBjDAQ2CgHoYBPMLERsUgFADQkWHABehYiDLiIAE0EC6EGpXgAVOAF4JAOggAjAFZY6MABRI4N23butWcFWs0AufQC0FAeRNmYAB4kARsADw9xPRoAVxBDLCgLAEoAGnsMzLhHWwivBQA5OISoVNC4Dkjo4sSU9KyG7KcbSvz5IvjEsptxKtjOpLTGhpyequ92mtKBXGTh+cac3AA+IRd1FB0UNDBMLAsNOYWm51UN5A8ANUYMYHlGGGhAjT9Tc2Dy46+HZrg8jQ6JW63xBJxaHgBU2BoK+owkEMBiU+MIWS2Wy1sazO8AASltkHpkAALMwAays3xy61xHkMEAg2EYNDgAFo4AAJYAAcyJcAACokCNBePQcMSybQuQBCcp5ACMwNaACZ0uVenAAMwzZJAA)
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ssl=17&ssc=3&pln=1&pc=1#code/JYWwDg9gTgLgBAbzgYQuYAbApnAvnAMyjTgHIABAZ2ADsBjDAQ2CgHoYBPMLERsUgFADQkWHABehYiDLiIAE0EDWrFGjCYcALQVwAKlyxC6EGpXjI4AXjXpsACnEA6CACMAVljox7SOP4DAoOCAlTgTMwsALjgANUYMYHlGGGgAHj0AeQ8vGDS-AX8ADxjnGgBXEFcsKHsASgAaEOaW1tDVYpi9ADlK6qgmwMK4DlKnCqqa+sG22dmw-1H9XsmB4fExif7pud3WhYkulf6BXDq6vcur-zDcAD47gOHr3YEI8zgAJWsUJ2QACy8AGtfC89mEAJIwGopYCmSgxACMAAYAPrIjHojHDEpwREzMGEm6sYZLABMBKJYLCOnkzRiABYMciQJQ4PY+GBiEU6usYgBmKlCsIGbgAWT4gUZLLZ-g5YC5EB5pzqQA)
```typescript
import { Compile } from '@sinclair/typemap'
import z from 'zod'
const T = Compile(z.object({ // const T: Validator<TObject<{
// Compile Zod Type
const C = Compile(z.object({ // const C: Validator<TObject<{
x: z.number(), // x: TNumber,
y: z.number(), // y: TNumber,
z: z.number(), // z: TNumber
})) // }>>
const R = T.Check({ // High Performance Check
x: 1,
y: 2,
z: 3
const R = C.Check({ // Iterations: 10_000_000
x: 1, //
y: 2, // Zod : 4000ms (approx)
z: 3 // TypeMap : 40ms (approx)
})
```
## Overview
TypeMap is a type mapping library developed for the [TypeBox](https://github.com/sinclairzx81/typebox), [Valibot](https://github.com/fabian-hiller/valibot) and [Zod](https://github.com/colinhacks/zod) runtime type libraries. It is designed to make these libraries cross compatible by structurally remapping types from one library to another. TypeMap also provides a uniform frontend syntax for creating types, as well as uniform type compilation for high-performance validation via the TypeBox compiler infrastructure.
TypeMap is built to be a simple tool to let Valibot and Zod to integrate with TypeBox and Json Schema infrastructure. It is also written to enable TypeBox to integrate with systems using Valibot and Zod for validation.
License: MIT
## Contents
- [Install](#Install)
- [Overview](#Overview)
- [Libraries](#Libraries)
- [TypeBox](#TypeBox)
- [Valibot](#Valibot)
- [Zod](#Zod)
- [Static](#Static)
- [Compile](#Compile)
- [Benchmark](#Benchmark)
- [Contribute](#Contribute)
## Libraries
TypeMap exports mapping functions named after the library they map for. Each function can accept a type from any other library, where the function will attempt to map the type or return a `never` representation if a mapping is not possible.
TypeMap, at its core, is a runtime type mapping library. It is specifically designed to transform types from one library into types for another.
TypeMap performs deep type mapping both statically (within the type system) and at runtime. As a rule, if a type cannot be mapped during traversal, the library function returns a never representation specific to the library being mapped for.
### TypeBox
Use the TypeBox function to map the parameter into a TypeBox type
Use the TypeBox function to map a parameter into a TypeBox type.
```typescript
import { TypeBox } from '@sinclair/typemap'
@@ -169,9 +188,90 @@ const C = Zod(z.boolean()) // const C: z.ZodBoolean (Zod)
const D = Zod('string[]') // const D: z.ZodArray<...> (Syntax)
```
## Syntax
TypeMap provides an optional syntax that can be used to construct runtime type representations. Syntax mapping is based on [Syntax Type](https://github.com/sinclairzx81/typebox?#syntax) provided by TypeBox.
### Types
Pass a constant string parameter with an embedded TypeScript type annotion to construct a type. If there is a syntax error, the function return type will be `never`.
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgLQgEzgXzgMyhEOAcgAEBnYAOwGMAbAQ2CgHoYBPMAUxHrCICh+zZnADCUTvRic49FOjjsusyhgAK9KGRnAYg6hEpl4EsgFda8ALzy0ACiJIAHgC44ARixEAlADowmtp2zm6emN5AA)
```typescript
import { Zod } from '@sinclair/typemap'
// Create a Zod type and Parse it
const result = Zod('{ x: 1 }').parse({ x: 1 })
```
### Options
Options can be passed on the last parameter of a syntax type. TypeMap understands Json Schema keyword constraints. Known constraints will be mapped to analogs structures for the target library if possible.
```typescript
import { Zod } from '@sinclair/typemap'
// const T = z.string().email()
const T = Zod('string', {
format: 'email'
})
// const S = z.object({ x: z.number() }).strict()
const S = Zod('{ x: number }', {
additionalProperties: false
})
```
### Parameters
Syntax types can be parameterized to accept exterior types. The following injects a Zod type into a Valibot vector structure.
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgNQIYBtgCMIwDRwBaEAJnAL5wBmUEIcA5AAIDOwAdgMbqrBQD0MAJ5gApiFRgGAKGn9+cAMIR2LGFACuneABVZnFWrg64AXiKkAFA3YaQWUVAYBKWfLgAFVCxbG4MCDgAZX1DeCCzFAxsXEskE3ICAAMkAA8ALmMCIUydAgAvXIok5yA)
```typescript
import { Valibot, Zod } from '@sinclair/typemap'
// Construct T
const T = Zod('number')
// Pass T to S
const S = Valibot({ T }, `{ x: T, y: T, z: T }`)
```
### Generics
Using type parameters, you can construct generic via functions.
[Example Link](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgFQJ5gKYCEIA8A0cAagIYA2wARhDIQFoQAmcAvnAGZQQhwDkAAgGdgAOwDGZEsCgB6GOgwgSYXgChVMmXADiGERijAxxDGJjQUCuAB84AMQCu4mMAgj1Yt4PhFT5qHAAvHAAPMhwGLgweoyCcBCUAFZ+AHwAFMgAXCgAlEEpxORUNGlI4SyEqnBwAAZIuNnIhKiNhABejaw1qjnqmnAAcg4glAYmZhZomB5e8EMjBr4TAcFL-hkKOLhpvCLDo1C8Ob0aWgDKMIYiAObj-pbTqp4i3nAXV9drFqt+0GkMjB23g+RxyQA)
```typescript
import { TypeBox, Valibot, Zod } from '@sinclair/typemap'
// Generic Vector Type | Function
const Vector = <T extends object>(T: T) => Valibot({ T },
`{ x: T, y: T, z: T }`
)
// Number Vector Type
const NumberVector = Vector(TypeBox('number'))
// String Vector Type
const StringVector = Vector(Zod('string'))
```
## Static
TypeMap can statically infer for TypeBox, Valibot, Zod and Syntax with the `Static` type.
Use Static to uniformly infer for syntax and library types
```typescript
import { type Static } from '@sinclair/typemap'
@@ -189,32 +289,33 @@ type S = Static<typeof S> // string[]
## Compile
TypeMap offers JIT compilation of TypeBox, Valibot, Zod and Syntax using the `Compile` function. This function will internally use the TypeBox TypeCompiler for high performance checking. This function will also gracefully degrade to dynamic checking if the runtime does not support JavaScript evaluation.
The `Compile` function returns a validator object that implements the [standard-schema](https://github.com/standard-schema/standard-schema) interface.
Use the Compile function to compile Zod and Valibot on TypeBox validation infrastructure.
```typescript
import { Compile } from '@sinclair/typemap'
import { Compile, Zod } from '@sinclair/typemap'
// Pass TypeBox, Valibot, Zod or Syntax to JIT Compile the type.
const V = Compile(`{
x: number
y: number,
z: number
}`)
// Compile Zod Type
const V = Compile(Zod(`{
x: number,
y: number,
z: number
}`))
// TypeMap Check Function
// TypeMap Interface
const R1 = V.Check({ x: 1, y: 2, z: 3 })
// Standard Schema Interface
// Standard Schema Interface Check Function
const R2 = V['~standard'].validate({ x: 1, y: 2, z: 3 })
```
## Benchmark
This project manages a benchmark that evaluates type-check performance using Zod, Valibot, and TypeBox validators. The benchmark is set up to run 10 million check operations per library-validator pairing and reports the elapsed time taken to complete.
This project manages a small benchmark that compares validation performance using Zod, Valibot, and TypeBox validators. For more comprehensive community benchmarks, refer to the [runtime-type-benchmarks](https://github.com/moltar/typescript-runtime-type-benchmarks) project.
### Type
### Test
Benchmarks are run for the following type.
@@ -224,7 +325,7 @@ type T = { x: number, y: string, z: boolean }
### Results
Results show validate performance for the type.
Results show the approximate elapsed time to complete the given iterations
```typescript
@@ -243,9 +344,9 @@ Results show validate performance for the type.
```
For community benchmarks, refer to the [runtime-type-benchmarks](https://github.com/moltar/typescript-runtime-type-benchmarks) project.
## Contribute
This project is open to community contributions. Please ensure you submit an open issue before creating a pull request. TypeMap encourages open community discussion before accepting new features.
This project is open to community contributions. Please ensure you submit an open issue before creating a pull request. TypeBox and associated projects preference open community discussion before accepting new features.

View File

@@ -29,8 +29,8 @@ THE SOFTWARE.
import { TypeCompiler, TypeCheck, ValueErrorIterator } from '@sinclair/typebox/compiler'
import { Value } from '@sinclair/typebox/value'
import { TypeBox, TTypeBox } from '../typebox/typebox'
import { StandardSchemaV1 } from './standard'
import { IsEvalSupported } from './environment'
import { StandardSchemaV1 } from './standard'
import * as t from '@sinclair/typebox'
// ------------------------------------------------------------------
@@ -112,9 +112,8 @@ function CompileDynamic<Type extends t.TSchema>(type: Type, references: t.TSchem
/** Compiles a type for high performance validation */
// prettier-ignore
type TCompile<Type extends object | string,
Schema extends t.TSchema = TTypeBox<Type>,
Result extends Validator<Schema> = Validator<Schema>
> = Result
Schema extends t.TSchema = TTypeBox<{}, Type>,
> = Validator<Schema>
/** Compiles a type for high performance validation */
// prettier-ignore
export function Compile<Type extends object | string>(type: Type): TCompile<Type> {

View File

@@ -30,20 +30,14 @@ import * as t from '@sinclair/typebox'
import * as v from 'valibot'
import * as z from 'zod'
// ------------------------------------------------------------------
// Syntax
// ------------------------------------------------------------------
/** Returns true if the given value is a Syntax type */
export type TIsSyntax<Type extends unknown> = Type extends string ? true : false
/** Returns true if the given value is a Syntax type */
export function IsSyntax(type: unknown): type is string {
return t.ValueGuard.IsString(type)
}
// ------------------------------------------------------------------
// TypeBox
// ------------------------------------------------------------------
/** Returns true if the given value is a TypeBox type */
export type TIsTypeBox<Type extends unknown> = Type extends t.TSchema ? true : false
// prettier-ignore
export type TIsTypeBox<Type extends unknown> = (
Type extends t.TSchema ? true : false
)
/** Returns true if the given value is a TypeBox type */
export function IsTypeBox(type: unknown): type is t.TSchema {
return t.KindGuard.IsSchema(type)
@@ -74,12 +68,44 @@ export function IsValibot(type: unknown): type is v.AnySchema {
// ------------------------------------------------------------------
// Zod
// ------------------------------------------------------------------
// prettier-ignore
/** Returns true if the given value is a Zod type */
// prettier-ignore
export type TIsZod<Type extends unknown> = (
Type extends z.ZodTypeAny ? true : false
)
/** Returns true if the given value is a Zod type */
// prettier-ignore
export function IsZod(type: unknown): type is z.ZodTypeAny {
return t.ValueGuard.IsObject(type) && t.ValueGuard.HasPropertyKey(type, '~standard') && t.ValueGuard.IsObject(type['~standard']) && t.ValueGuard.HasPropertyKey(type['~standard'], 'vendor') && type['~standard'].vendor === 'zod'
return (
t.ValueGuard.IsObject(type) &&
t.ValueGuard.HasPropertyKey(type, '~standard') &&
t.ValueGuard.IsObject(type['~standard']) &&
t.ValueGuard.HasPropertyKey(type['~standard'], 'vendor') &&
type['~standard'].vendor === 'zod'
)
}
// ------------------------------------------------------------------
// Signature
// ------------------------------------------------------------------
function Signature1(args: any[]) {
return args.length === 3 && t.ValueGuard.IsObject(args[0]) && t.ValueGuard.IsString(args[1]) && t.ValueGuard.IsObject(args[2])
}
function Signature2(args: any[]) {
return args.length === 2 && t.ValueGuard.IsString(args[0]) && t.ValueGuard.IsObject(args[1])
}
function Signature3(args: any[]) {
return args.length === 2 && t.ValueGuard.IsObject(args[0]) && t.ValueGuard.IsString(args[1])
}
function Signature4(args: any[]) {
return args.length === 1 && (t.ValueGuard.IsString(args[0]) || t.ValueGuard.IsObject(args[0]))
}
export function Signature(args: any[]): [parameter: Record<PropertyKey, object>, type: string | object, options: object] {
// prettier-ignore
return (
Signature1(args) ? [args[0], args[1], args[2]] :
Signature2(args) ? [{}, args[0], args[1]] :
Signature3(args) ? [args[0], args[1], {}] :
Signature4(args) ? [{}, args[0], {}] :
[{}, 'never', {}]
)
}

View File

@@ -27,6 +27,7 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/
export { type Static } from './static'
export { type TSyntaxOptions } from './options'
export * from './compile/compile'
export * from './typebox/typebox'
export * from './valibot/valibot'

40
src/options.ts Normal file
View File

@@ -0,0 +1,40 @@
/*--------------------------------------------------------------------------
@sinclair/typemap
The MIT License (MIT)
Copyright (c) 2024-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------------------------------------------------------------*/
import * as t from '@sinclair/typebox'
/** Syntax Options and Constraints */
// prettier-ignore
export type TSyntaxOptions = (
t.ObjectOptions
& t.ArrayOptions
& t.NumberOptions
& t.IntegerOptions
& t.StringOptions
& t.DateOptions
)

View File

@@ -29,16 +29,18 @@ THE SOFTWARE.
import { StaticParseAsSchema, Parse } from '@sinclair/typebox/syntax'
import * as t from '@sinclair/typebox'
// ------------------------------------------------------------------
// TypeBoxFromSyntax
// ------------------------------------------------------------------
// prettier-ignore
export type TTypeBoxFromSyntax<
Type extends string | object,
Parsed = Type extends string ? StaticParseAsSchema<{}, Type> : t.TNever,
Result extends t.TSchema = Parsed extends t.TSchema ? Parsed : t.TNever
export type TTypeBoxFromSyntax<Context extends t.TProperties, Type extends string | object,
Mapped = Type extends string ? StaticParseAsSchema<Context, Type> : t.TNever,
Result extends t.TSchema = Mapped extends t.TSchema ? Mapped : t.TNever
> = Result
// prettier-ignore
export function TypeBoxFromSyntax<Type extends string | object>(type: Type): TTypeBoxFromSyntax<Type> {
const parsed = t.ValueGuard.IsString(type) ? Parse(type) : t.Never()
/** Creates a TypeBox Type From Syntax */
export function TypeBoxFromSyntax<Context extends t.TProperties, Type extends string>(context: Context, type: Type, options?: t.SchemaOptions): TTypeBoxFromSyntax<Context, Type> {
const parsed = t.ValueGuard.IsString(type) ? Parse(context, type, options) : t.Never()
const result = t.KindGuard.IsSchema(parsed) ? parsed : t.Never()
return result as never
}

View File

@@ -26,39 +26,77 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/
import { TTypeBoxFromSyntax, TypeBoxFromSyntax } from './typebox-from-syntax'
import { TTypeBoxFromTypeBox, TypeBoxFromTypeBox } from './typebox-from-typebox'
import { TTypeBoxFromValibot, TypeBoxFromValibot } from './typebox-from-valibot'
import { TTypeBoxFromZod, TypeBoxFromZod } from './typebox-from-zod'
import * as Guard from '../guard'
import { type TTypeBoxFromSyntax, TypeBoxFromSyntax } from './typebox-from-syntax'
import { type TTypeBoxFromTypeBox, TypeBoxFromTypeBox } from './typebox-from-typebox'
import { type TTypeBoxFromValibot, TypeBoxFromValibot } from './typebox-from-valibot'
import { type TTypeBoxFromZod, TypeBoxFromZod } from './typebox-from-zod'
import { type TSyntaxOptions } from '../options'
import * as g from '../guard'
import * as t from '@sinclair/typebox'
// ------------------------------------------------------------------------------
//
// TParameter: Shared
//
// TypeBox supports Type injection via a Context parameter. Because the Context
// only accepts types of TSchema, we need to an intermediate structure to hold
// remote types such that they can be mapped prior to syntax parsing.
//
// -------------------------------------------------------------------------------
export type TParameter = Record<PropertyKey, object>
// ------------------------------------------------------------------
// ContextFromParameter
// ------------------------------------------------------------------
// prettier-ignore
export type TContextFromParameter<Parameter extends TParameter,
Result extends t.TProperties = {
[Key in keyof Parameter]: TTypeBox<{}, Parameter[Key]>
}
> = Result
// prettier-ignore
export function ContextFromParameter<Parameter extends TParameter>(parameter: Parameter): TContextFromParameter<Parameter> {
return globalThis.Object.getOwnPropertyNames(parameter).reduce((result, key) => {
return { ...result, [key]: TypeBox(parameter[key] as never) }
}, {} as t.TProperties) as never
}
// ------------------------------------------------------------------
// TypeBox
// ------------------------------------------------------------------
/** Creates a TypeBox type from Syntax or another Type */
// prettier-ignore
export type TTypeBox<Type extends object | string, Result = (
Guard.TIsSyntax<Type> extends true ? TTypeBoxFromSyntax<Type> :
Guard.TIsTypeBox<Type> extends true ? TTypeBoxFromTypeBox<Type> :
Guard.TIsValibot<Type> extends true ? TTypeBoxFromValibot<Type> :
Guard.TIsZod<Type> extends true ? TTypeBoxFromZod<Type> :
export type TTypeBox<Parameter extends TParameter, Type extends object | string, Result = (
Type extends string ? TTypeBoxFromSyntax<TContextFromParameter<Parameter>, Type> :
g.TIsTypeBox<Type> extends true ? TTypeBoxFromTypeBox<Type> :
g.TIsValibot<Type> extends true ? TTypeBoxFromValibot<Type> :
g.TIsZod<Type> extends true ? TTypeBoxFromZod<Type> :
t.TNever
)> = Result
/** Creates a TypeBox type from Syntax or another Type */
export function TypeBox<Parameter extends TParameter, Type extends string>(parameter: Parameter, type: Type, options?: TSyntaxOptions): TTypeBox<Parameter, Type>
/** Creates a TypeBox type from Syntax or another Type */
export function TypeBox<Type extends object | string>(type: Type, options?: TSyntaxOptions): TTypeBox<{}, Type>
/** Creates a TypeBox type from Syntax or another Type */
// prettier-ignore
export function TypeBox<Type extends object | string>(type: Type): TTypeBox<Type> {
return (
Guard.IsSyntax(type) ? TypeBoxFromSyntax(type) :
Guard.IsTypeBox(type) ? TypeBoxFromTypeBox(type) :
Guard.IsValibot(type) ? TypeBoxFromValibot(type) :
Guard.IsZod(type) ? TypeBoxFromZod(type) :
t.Never()
) as never
export function TypeBox(...args: any[]): never {
const [parameter, type, options] = g.Signature(args)
return t.CloneType(
t.ValueGuard.IsString(type) ? TypeBoxFromSyntax(ContextFromParameter(parameter), type, options) :
g.IsTypeBox(type) ? TypeBoxFromTypeBox(type) :
g.IsValibot(type) ? TypeBoxFromValibot(type) :
g.IsZod(type) ? TypeBoxFromZod(type) :
t.Never(),
options) as never
}
/**
* Creates a TypeBox type from Syntax or another Type
* @deprecated Use TypeBox() export instead
*/
export function Box<Type extends object | string, Mapped = TTypeBox<Type>, Result extends Mapped = Mapped>(type: Type): Result {
return TypeBox(type) as never
}
/** Creates a TypeBox type from Syntax or another Type */
export function Type<Parameter extends TParameter, Type extends string>(parameter: Parameter, type: Type, options?: TSyntaxOptions): TTypeBox<Parameter, Type>
/** Creates a TypeBox type from Syntax or another Type */
export function Type<Type extends object | string>(type: Type, options?: TSyntaxOptions): TTypeBox<{}, Type>
/** Creates a TypeBox type from Syntax or another Type */
// prettier-ignore
export function Type(...args: any[]): never {
return TypeBox.apply(null, args as never) as never
}

View File

@@ -32,15 +32,18 @@ import { ValibotFromTypeBox, TValibotFromTypeBox } from './valibot-from-typebox'
import * as t from '@sinclair/typebox'
import * as c from './common'
// ------------------------------------------------------------------
// ValibotFromSyntax
// ------------------------------------------------------------------
// prettier-ignore
export type TValibotFromSyntax<Type extends object | string,
Schema extends t.TSchema = TTypeBoxFromSyntax<Type>,
export type TValibotFromSyntax<Context extends t.TProperties, Type extends string,
Schema extends t.TSchema = TTypeBoxFromSyntax<Context, Type>,
Result extends c.BaseSchema = TValibotFromTypeBox<Schema>
> = Result
// prettier-ignore
export function ValibotFromSyntax<Type extends object | string>(type: Type): TValibotFromSyntax<Type> {
const schema = TypeBoxFromSyntax(type)
export function ValibotFromSyntax<Context extends t.TProperties, Type extends string>(context: Context, type: Type, options?: t.SchemaOptions): TValibotFromSyntax<Context, Type> {
const schema = TypeBoxFromSyntax(context, type, options)
const result = ValibotFromTypeBox(schema)
return result
return result as never
}

View File

@@ -30,27 +30,42 @@ import { type TValibotFromSyntax, ValibotFromSyntax } from './valibot-from-synta
import { type TValibotFromTypeBox, ValibotFromTypeBox } from './valibot-from-typebox'
import { type TValibotFromValibot, ValibotFromValibot } from './valibot-from-valibot'
import { type TValibotFromZod, ValibotFromZod } from './valibot-from-zod'
import * as Guard from '../guard'
import { type TSyntaxOptions } from '../options'
import * as g from '../guard'
import * as t from '@sinclair/typebox'
import * as v from 'valibot'
import * as c from './common'
import { TParameter, TContextFromParameter, ContextFromParameter } from '../typebox/typebox'
// ------------------------------------------------------------------
// Valibot
// ------------------------------------------------------------------
/** Creates a Valibot type from Syntax or another Type */
// prettier-ignore
export type TValibot<Type extends object | string, Result = (
Guard.TIsSyntax<Type> extends true ? TValibotFromSyntax<Type> :
Guard.TIsTypeBox<Type> extends true ? TValibotFromTypeBox<Type> :
Guard.TIsValibot<Type> extends true ? TValibotFromValibot<Type> :
Guard.TIsZod<Type> extends true ? TValibotFromZod<Type> :
export type TValibot<Parameter extends TParameter, Type extends object | string, Result = (
Type extends string ? TValibotFromSyntax<TContextFromParameter<Parameter>, Type> :
g.TIsTypeBox<Type> extends true ? TValibotFromTypeBox<Type> :
g.TIsValibot<Type> extends true ? TValibotFromValibot<Type> :
g.TIsZod<Type> extends true ? TValibotFromZod<Type> :
v.NeverSchema<c.BaseError>
)> = Result
/** Creates a Valibot type from Syntax or another Type */
export function Valibot<Parameter extends TParameter, Type extends string>(parameter: Parameter, type: Type, options?: TSyntaxOptions): TValibot<Parameter, Type>
/** Creates a Valibot type from Syntax or another Type */
export function Valibot<Type extends string>(type: Type, options?: TSyntaxOptions): TValibot<{}, Type>
/** Creates a Valibot type from Syntax or another Type */
export function Valibot<Type extends object>(type: Type, options?: TSyntaxOptions): TValibot<{}, Type>
/** Creates a Valibot type from Syntax or another Type */
// prettier-ignore
export function Valibot<Type extends object | string, Mapped = TValibot<Type>, Result extends Mapped = Mapped>(type: Type): Result {
export function Valibot(...args: any[]): never {
const [parameter, type, options] = g.Signature(args)
return (
Guard.IsSyntax(type) ? ValibotFromSyntax(type) :
Guard.IsTypeBox(type) ? ValibotFromTypeBox(type) :
Guard.IsValibot(type) ? ValibotFromValibot(type) :
Guard.IsZod(type) ? ValibotFromZod(type as any) :
t.ValueGuard.IsString(type) ? ValibotFromSyntax(ContextFromParameter(parameter), type, options) :
g.IsTypeBox(type) ? ValibotFromTypeBox(type) :
g.IsValibot(type) ? ValibotFromValibot(type) :
g.IsZod(type) ? ValibotFromZod(type as any) :
v.never()
) as never
}

View File

@@ -31,15 +31,17 @@ import { ZodFromTypeBox, TZodFromTypeBox } from './zod-from-typebox'
import * as t from '@sinclair/typebox'
import * as z from 'zod'
// ------------------------------------------------------------------
// ZodFromSyntax
// ------------------------------------------------------------------
// prettier-ignore
export type TZodFromSyntax<Type extends object | string,
Schema extends t.TSchema = TTypeBoxFromSyntax<Type>,
export type TZodFromSyntax<Context extends t.TProperties, Type extends string,
Schema extends t.TSchema = TTypeBoxFromSyntax<Context, Type>,
Result extends z.ZodTypeAny | z.ZodEffects<any> = TZodFromTypeBox<Schema>
> = Result
// prettier-ignore
export function ZodFromSyntax<Type extends string>(type: Type): TZodFromSyntax<Type> {
const schema = TypeBoxFromSyntax(type)
export function ZodFromSyntax<Context extends t.TProperties, Type extends string>(context: Context, type: Type, options?: t.SchemaOptions): TZodFromSyntax<Context, Type> {
const schema = TypeBoxFromSyntax(context, type, options)
const result = ZodFromTypeBox(schema)
return result
return result as never
}

View File

@@ -30,27 +30,42 @@ import { type TZodFromSyntax, ZodFromSyntax } from './zod-from-syntax'
import { type TZodFromTypeBox, ZodFromTypeBox } from './zod-from-typebox'
import { type TZodFromValibot, ZodFromValibot } from './zod-from-valibot'
import { type TZodFromZod, ZodFromZod } from './zod-from-zod'
import * as Guard from '../guard'
import { type TSyntaxOptions } from '../options'
import * as g from '../guard'
import * as t from '@sinclair/typebox'
import * as z from 'zod'
import { TParameter, TContextFromParameter, ContextFromParameter } from '../typebox/typebox'
// ------------------------------------------------------------------
// Zod
// ------------------------------------------------------------------
/** Creates a Zod type from Syntax or another Type */
// prettier-ignore
export type TZod<Type extends object | string, Result = (
Guard.TIsSyntax<Type> extends true ? TZodFromSyntax<Type> :
Guard.TIsTypeBox<Type> extends true ? TZodFromTypeBox<Type> :
Guard.TIsValibot<Type> extends true ? TZodFromValibot<Type> :
Guard.TIsZod<Type> extends true ? TZodFromZod<Type> :
export type TZod<Parameter extends TParameter, Type extends object | string, Result = (
Type extends string ? TZodFromSyntax<TContextFromParameter<Parameter>, Type> :
g.TIsTypeBox<Type> extends true ? TZodFromTypeBox<Type> :
g.TIsValibot<Type> extends true ? TZodFromValibot<Type> :
g.TIsZod<Type> extends true ? TZodFromZod<Type> :
z.ZodNever
)> = Result
/** Creates a Zod type from Syntax or another Type */
export function Zod<Parameter extends TParameter, Type extends string>(parameter: Parameter, type: Type, options?: TSyntaxOptions): TZod<Parameter, Type>
/** Creates a Zod type from Syntax or another Type */
export function Zod<Type extends string>(type: Type, options?: TSyntaxOptions): TZod<{}, Type>
/** Creates a Zod type from Syntax or another Type */
export function Zod<Type extends object>(type: Type, options?: TSyntaxOptions): TZod<{}, Type>
/** Creates a Zod type from Syntax or another Type */
// prettier-ignore
export function Zod<Type extends object | string, Mapped = TZod<Type>, Result extends Mapped = Mapped>(type: Type): Result {
export function Zod(...args: any[]): never {
const [parameter, type, options] = g.Signature(args)
return (
Guard.IsSyntax(type) ? ZodFromSyntax(type) :
Guard.IsTypeBox(type) ? ZodFromTypeBox(type) :
Guard.IsValibot(type) ? ZodFromValibot(type) :
Guard.IsZod(type) ? ZodFromZod(type) :
t.ValueGuard.IsString(type) ? ZodFromSyntax(ContextFromParameter(parameter), type, options) :
g.IsTypeBox(type) ? ZodFromTypeBox(type) :
g.IsValibot(type) ? ZodFromValibot(type) :
g.IsZod(type) ? ZodFromZod(type) :
z.never()
) as never
}

View File

@@ -1,3 +1,5 @@
import './options'
import './parameters'
import './typebox-from-zod'
import './typebox-from-valibot'
import './valibot-from-typebox'

17
test/options.ts Normal file
View File

@@ -0,0 +1,17 @@
import { Assert } from './assert'
import { TypeBox, Valibot, Zod } from '@sinclair/typemap'
describe('SyntaxOptions', () => {
it('Should map Options (Zod)', () => {
const A = TypeBox('string', { minLength: 10 })
const B = Zod(A)
const C = TypeBox(B)
Assert.IsEqual(C.minLength, 10)
})
it('Should map Options (Valibot)', () => {
const A = TypeBox('string', { minLength: 10 })
const B = Valibot(A)
const C = TypeBox(B)
Assert.IsEqual(C.minLength, 10)
})
})

32
test/parameters.ts Normal file
View File

@@ -0,0 +1,32 @@
import { Assert } from './assert'
import { TypeBox, Valibot, Zod } from '@sinclair/typemap'
import { KindGuard } from '@sinclair/typebox'
describe('Parameters', () => {
it('Should map Parameters (Zod)', () => {
const A = TypeBox('string')
const B = Zod({ A }, 'A')
const C = TypeBox(B)
Assert.IsTrue(KindGuard.IsString(C))
})
it('Should map Parameters (Valibot)', () => {
const A = TypeBox('string')
const B = Valibot({ A }, 'A')
const C = TypeBox(B)
Assert.IsTrue(KindGuard.IsString(C))
})
it('Should map Parameters With Constraints (Zod)', () => {
const A = TypeBox('string', { minLength: 10 })
const B = Zod({ A }, 'A')
const C = TypeBox(B)
Assert.IsTrue(KindGuard.IsString(C))
Assert.IsEqual(C.minLength, 10)
})
it('Should map Parameters With Constraints (Valibot)', () => {
const A = TypeBox('string', { minLength: 10 })
const B = Valibot({ A }, 'A')
const C = TypeBox(B)
Assert.IsTrue(KindGuard.IsString(C))
Assert.IsEqual(C.minLength, 10)
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 KiB

After

Width:  |  Height:  |  Size: 698 KiB