diff --git a/design/typemap.blend b/design/typemap.blend index 97b2d46..004d1ca 100644 Binary files a/design/typemap.blend and b/design/typemap.blend differ diff --git a/design/typemap.blend1 b/design/typemap.blend1 index c526491..97b2d46 100644 Binary files a/design/typemap.blend1 and b/design/typemap.blend1 differ diff --git a/example/index.ts b/example/index.ts index b12032a..f5c3b38 100644 --- a/example/index.ts +++ b/example/index.ts @@ -29,5 +29,4 @@ const Z = Zod(S) // const Z: ZodObject<{ // x: ZodNumber, // y: ZodNumber, // z: ZodNumber - // }, ...> - + // }, ...> \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 177b0dd..fd61cce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typemap", - "version": "0.8.6", + "version": "0.8.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typemap", - "version": "0.8.6", + "version": "0.8.7", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.17.2", diff --git a/package.json b/package.json index 1ac8878..f0438cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typemap", - "version": "0.8.6", + "version": "0.8.7", "description": "Unified Syntax and Type Compiler for Runtime Types", "author": "sinclairzx81", "license": "MIT", diff --git a/readme.md b/readme.md index e40fa8a..87c18b9 100644 --- a/readme.md +++ b/readme.md @@ -24,23 +24,25 @@ $ npm install @sinclair/typemap --save ## Usage -Parse and Compile Types from TypeScript syntax ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgYQuYAbApnAvnAMyjTgHIABAZ2ADsBjDAQ2CgHoYBPMLERsUgFAC6EGpXhQslAK4Z4AXhRowmLAApS4qLQDmcAD5wasjKQCUAOgAKjKJXWkAElgwYIcAOrQMAE3MDWVjhg4MAgUhCIyMjwqNi48ICgiJi42MAUUkAAUgy4MFt7OEoOGhhGAA90rOzcuxwAN0YMaSxE6JaIzOyRMQkpWRgALgKYbRo9Q2NXOEUnFzdPbz8gA)) +Parse and Compile Types from TypeScript syntax ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgLQgEzgXzgMyhEOAcgAEBnYAOwGMAbAQ2CgHoYBPMAUxHrCICg4Q4SNHD+1CJTLwonMgFda8ALwp0ACiIyoVAOZwAPnEpLaRAJQA6MPShlOWgCodOAIQgAPOAHV6ZOAAJTjlLfmZmUUAgUjFYmNiEsRjwyJF4xKF0jMTAFFJAAFICuFt7TjgAN3paBU4U6OzM+oTatMb8vKK7BwAuOAAvKx19DWtoDX7TWlphi1Fm4Si5oTa4SWlZeSUYHsHKA2MJ2jg1ImDJiF9oWjQiIA)) ```typescript -import { Compile } from '@sinclair/typemap' - -const result = Compile('string | null').Parse('Hello World') -// │ │ │ -// │ └── parse syntax └─── parse value +import { Zod } from '@sinclair/typemap' + +const result = Zod('string | null').parse('TypeBox Was Here') +// │ │ │ +// │ │ └─── parse value +// │ │ +// │ └── parse: z.string().or(z.null()) // │ // └── const result: string | null = 'Hello World' ``` ## Overview -TypeMap is an 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 common TypeScript syntax for type construction across libraries, a runtime compiler for high-performance validation and provides type translation from one library to another. +TypeMap is an 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) libraries. It provides a common TypeScript syntax for type construction, a runtime compiler for high-performance validation and provides type translation from one library to another. TypeMap is built using components provided by the TypeBox library and infrastructure. -TypeMap is written to be an advanced adapter and type translation system for the TypeBox project. It is designed specifically to integrate and accelerate remote type libraries on Json Schema compatible infrastructure as well as to enable TypeBox schematics to be remapped to remote type library infrastructure. This project also provides high-performance validation for frameworks that orientate around the [Standard Schema](https://github.com/standard-schema/standard-schema) TypeScript interface. +TypeMap is written to be an advanced adapter and type translation system for the [TypeBox](https://github.com/sinclairzx81/typebox) project. It is designed specifically to integrate and accelerate remote type libraries on Json Schema compatible infrastructure as well as to enable TypeBox schematics to be remapped to remote type library infrastructure. This project also provides high-performance validation for frameworks that orientate around the [Standard Schema](https://github.com/standard-schema/standard-schema) TypeScript interface. License: MIT @@ -60,6 +62,7 @@ License: MIT - [Parameters](#Parameters) - [Generics](#Generics) - [Static](#Static) +- [TreeShake](#TreeShake) - [Compile](#Compile) - [Benchmark](#Benchmark) - [Contribute](#Contribute) @@ -151,7 +154,7 @@ const R = C.Check({ // Iterations: 10_000_000 ## Mapping -TypeMap is primarily a mapping system used for type translation. It provides a mapping function per library which is used to translate remote types into types specific to that library. If no translation is possible, these functions return a `never` representation specific to the library being mapped. +TypeMap is primarily a mapping system intended for type translation. It provides mapping functions per library which is used to translate remote types into types specific to that library. All mapping functions make a best attempt to retain semantics from each library. If no translation is possible, these functions return a `never` representation specific to the library being mapped. ### TypeBox @@ -195,7 +198,9 @@ const Z = Zod(z.boolean()) // const Z: z.ZodBoolean ## Syntax -TypeMap provides a TypeScript syntax parser that can be used to create library types. TypeScript parsing is implemented at runtime as well as in the TypeScript type system. It is provided as a convenient means of creating and composing library types under a common syntax. Be mindful, syntax parsing in the type system can reduce inference performance. +TypeMap provides a TypeScript syntax parser that can be used to create library types. TypeScript parsing is implemented at runtime as well as in the TypeScript type system. It offers a convenient means of creating cross library types without having to author across multiple type builder API. Please be mindful using this feature, while syntax parsing has the potential to dramatically improve developer experience, advanced DX mapping in the type system usually has an inference overhead, and parsing in the type system may significantly reduce inference performance. Use with consideration. + +Technical Reference [ParseBox](https://github.com/sinclairzx81/parsebox) > [Syntax Types](https://github.com/sinclairzx81/typebox?tab=readme-ov-file#syntax) ### Types @@ -287,10 +292,26 @@ const V = v.string() // Valibot const Z = z.boolean() // Zod const S = 'string[]' // Syntax +type S = Static // string[] type T = Static // number type V = Static // string type Z = Static // boolean -type S = Static // string[] +``` + +## TreeShake + +TypeMap exports the top-level TypeBox, Valibot, and Zod functions, which broadly translate any type. However, applications are most likely going to be interested in translating between two libraries (at most) and in one specific direction (e.g., Zod to TypeBox). TypeMap provides specific functions that perform only a particular translation. Using these specific mapping functions allows bundlers to tree-shake unused type libraries. + +```typescript +import { TypeBoxFromZod } from '@sinclair/typemap' // Bundle TypeBox | Zod, Tree Shake Valibot + +import * as z from 'zod' + +const T = TypeBoxFromZod(z.object({ // const T: TObject<{ + x: z.number(), // x: TNumber; + y: z.number(), // y: TNumber; + z: z.number() // z: TNumber; +})) // }> ``` ## Compile diff --git a/src/guard.ts b/src/guard.ts index 439c27e..f0a7ea5 100644 --- a/src/guard.ts +++ b/src/guard.ts @@ -30,14 +30,22 @@ import * as t from '@sinclair/typebox' import * as v from 'valibot' import * as z from 'zod' +// ------------------------------------------------------------------ +// Syntax +// ------------------------------------------------------------------ +/** Structural Type for Syntax */ +export type SyntaxType = string + +export function IsSyntax(value: unknown): value is string { + return t.ValueGuard.IsString(value) +} + // ------------------------------------------------------------------ // TypeBox // ------------------------------------------------------------------ -/** Returns true if the given value is a TypeBox type */ -// prettier-ignore -export type TIsTypeBox = ( - Type extends t.TSchema ? true : false -) +/** Structural Type for TypeBox */ +export type TypeBoxType = t.TSchema + /** Returns true if the given value is a TypeBox type */ export function IsTypeBox(type: unknown): type is t.TSchema { return t.KindGuard.IsSchema(type) @@ -45,15 +53,9 @@ export function IsTypeBox(type: unknown): type is t.TSchema { // ------------------------------------------------------------------ // Valibot // ------------------------------------------------------------------ -/** Returns true if the given value is a Valibot type */ -// prettier-ignore -export type TIsValibot = ( - Type extends v.BaseSchema> - ? Type extends { '~standard': { vendor: 'valibot' } } - ? true - : false - : false -) +/** Structural Type for Valibot */ +export type ValibotType = v.BaseSchema> + /** Returns true if the given value is a Valibot type */ // prettier-ignore export function IsValibot(type: unknown): type is v.AnySchema { @@ -68,6 +70,9 @@ export function IsValibot(type: unknown): type is v.AnySchema { // ------------------------------------------------------------------ // Zod // ------------------------------------------------------------------ +/** Structural Type for Zod */ +export type ZodType = z.ZodTypeAny | z.ZodEffects + /** Returns true if the given value is a Zod type */ // prettier-ignore export type TIsZod = ( diff --git a/src/index.ts b/src/index.ts index 95662ca..b220f19 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,9 +26,41 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { type Static } from './static' export { type TSyntaxOptions } from './options' + +// ------------------------------------------------------------------ +// Static +// ------------------------------------------------------------------ +export { type Static } from './static' + +// ------------------------------------------------------------------ +// Compile +// ------------------------------------------------------------------ export * from './compile/compile' -export * from './typebox/typebox' -export * from './valibot/valibot' -export * from './zod/zod' + +// ------------------------------------------------------------------ +// TypeBox +// ------------------------------------------------------------------ +export * from './typebox/typebox-from-syntax' +export * from './typebox/typebox-from-typebox' +export * from './typebox/typebox-from-valibot' +export * from './typebox/typebox-from-zod' +export { type TTypeBox, TypeBox } from './typebox/typebox' + +// ------------------------------------------------------------------ +// Valibot +// ------------------------------------------------------------------ +export * from './valibot/valibot-from-syntax' +export * from './valibot/valibot-from-typebox' +export * from './valibot/valibot-from-valibot' +export * from './valibot/valibot-from-zod' +export { type TValibot, Valibot } from './valibot/valibot' + +// ------------------------------------------------------------------ +// Zod +// ------------------------------------------------------------------ +export * from './zod/zod-from-syntax' +export * from './zod/zod-from-typebox' +export * from './zod/zod-from-valibot' +export * from './zod/zod-from-zod' +export { type TZod, Zod } from './zod/zod' diff --git a/src/typebox/typebox-from-syntax.ts b/src/typebox/typebox-from-syntax.ts index 4c964cf..dfca9ec 100644 --- a/src/typebox/typebox-from-syntax.ts +++ b/src/typebox/typebox-from-syntax.ts @@ -32,9 +32,10 @@ import * as t from '@sinclair/typebox' // ------------------------------------------------------------------ // TypeBoxFromSyntax // ------------------------------------------------------------------ + // prettier-ignore -export type TTypeBoxFromSyntax : t.TNever, +export type TTypeBoxFromSyntax, Result extends t.TSchema = Mapped extends t.TSchema ? Mapped : t.TNever > = Result diff --git a/src/typebox/typebox-from-typebox.ts b/src/typebox/typebox-from-typebox.ts index 8d8ea2d..e59474d 100644 --- a/src/typebox/typebox-from-typebox.ts +++ b/src/typebox/typebox-from-typebox.ts @@ -28,11 +28,14 @@ THE SOFTWARE. import * as t from '@sinclair/typebox' +// ------------------------------------------------------------------ +// TypeBoxFromTypeBox +// ------------------------------------------------------------------ + // prettier-ignore -export type TTypeBoxFromTypeBox = ( - Type extends t.TSchema ? Type : t.TNever -) +export type TTypeBoxFromTypeBox = Type + // prettier-ignore -export function TypeBoxFromTypeBox = TTypeBoxFromTypeBox>(type: Type): Result { +export function TypeBoxFromTypeBox = TTypeBoxFromTypeBox>(type: Type): Result { return (t.KindGuard.IsSchema(type) ? type : t.Never()) as never } diff --git a/src/typebox/typebox-from-valibot.ts b/src/typebox/typebox-from-valibot.ts index faf2e30..3536cf7 100644 --- a/src/typebox/typebox-from-valibot.ts +++ b/src/typebox/typebox-from-valibot.ts @@ -239,7 +239,7 @@ function FromBoolean(type: BaseSchema): t.TSchema { // Custom // ------------------------------------------------------------------ t.TypeRegistry.Set('ValibotCustom', (schema, value) => v.safeParse(schema.schema, value).success) -export interface TCustom = v.CustomSchema> extends t.TSchema { +interface TCustom = v.CustomSchema> extends t.TSchema { [t.Kind]: 'ValibotCustom' static: v.InferOutput type: Type @@ -279,54 +279,54 @@ function FromEnum(type: Type): t.TSchema { // ------------------------------------------------------------------ // File // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotFile', (schema, value) => { +t.TypeRegistry.Set('ValibotFile', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TFile = v.FileSchema> extends t.TSchema { +export interface TValibotFile = v.FileSchema> extends t.TSchema { [t.Kind]: 'ValibotFile' static: v.InferOutput type: Type } -function _File(type: v.FileSchema, options?: t.SchemaOptions): TFile { +function _File(type: v.FileSchema, options?: t.SchemaOptions): TValibotFile { return t.CreateType({ [t.Kind]: 'ValibotFile', type }, options) as never } -type TFromFile> = t.Ensure> +type TFromFile> = t.Ensure> function FromFile(type: BaseSchema): t.TSchema { return _File(type as v.FileSchema, Options(type)) } // ------------------------------------------------------------------ // Function // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotFunction', (schema, value) => { +t.TypeRegistry.Set('ValibotFunction', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TFunction = v.FunctionSchema> extends t.TSchema { +export interface TValibotFunction = v.FunctionSchema> extends t.TSchema { [t.Kind]: 'ValibotFunction' static: v.InferOutput type: Type } -function _Function>(type: Type, options?: t.SchemaOptions): TFunction { +function _Function>(type: Type, options?: t.SchemaOptions): TValibotFunction { return t.CreateType({ [t.Kind]: 'ValibotFunction', type }, options) as never } -type TFromFunction> = t.Ensure> +type TFromFunction> = t.Ensure> function FromFunction(type: BaseSchema): t.TSchema { return _Function(type as v.FunctionSchema, Options(type)) } // ------------------------------------------------------------------ // Instance // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotInstance', (schema, value) => { +t.TypeRegistry.Set('ValibotInstance', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TInstance = v.InstanceSchema> extends t.TSchema { +export interface TValibotInstance = v.InstanceSchema> extends t.TSchema { [t.Kind]: 'ValibotInstance' static: v.InferOutput type: Type } -function Instance>(type: Type, options?: t.SchemaOptions): TInstance { +function Instance>(type: Type, options?: t.SchemaOptions): TValibotInstance { return t.CreateType({ [t.Kind]: 'ValibotInstance', type }, options) as never } -type TFromInstance> = t.Ensure> +type TFromInstance> = t.Ensure> function FromInstance(type: BaseSchema): t.TSchema { return Instance(type as v.InstanceSchema, Options(type)) } @@ -375,54 +375,54 @@ function FromLooseObject(type: BaseSchema): t.TSchema { // ------------------------------------------------------------------ // LooseTuple // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotLooseTuple', (schema, value) => { +t.TypeRegistry.Set('ValibotLooseTuple', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TLooseTuple = v.LooseTupleSchema> extends t.TSchema { +export interface TValibotLooseTuple = v.LooseTupleSchema> extends t.TSchema { [t.Kind]: 'ValibotLooseTuple' static: v.InferOutput type: Type } -function LooseTuple>(type: Type, schema?: t.SchemaOptions): TLooseTuple { +function LooseTuple>(type: Type, schema?: t.SchemaOptions): TValibotLooseTuple { return t.CreateType({ [t.Kind]: 'ValibotLooseTuple', type }) as never } -type TFromLooseTuple> = t.Ensure> +type TFromLooseTuple> = t.Ensure> function FromLooseTuple(type: BaseSchema): t.TSchema { return LooseTuple(type as v.LooseTupleSchema, Options(type)) } // ------------------------------------------------------------------ // Map // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotMap', (schema, value) => { +t.TypeRegistry.Set('ValibotMap', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TMap = v.MapSchema> extends t.TSchema { +export interface TValibotMap = v.MapSchema> extends t.TSchema { [t.Kind]: 'ValibotMap' static: v.InferOutput type: Type } -function _Map>(type: Type, options?: t.SchemaOptions): TMap { +function _Map>(type: Type, options?: t.SchemaOptions): TValibotMap { return t.CreateType({ [t.Kind]: 'ValibotMap', type }) as never } -type TFromMap> = t.Ensure> +type TFromMap> = t.Ensure> function FromMap(type: BaseSchema): t.TSchema { return _Map(type as v.MapSchema, Options(type)) } // ------------------------------------------------------------------ // NaN // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotNaN', (schema, value) => { +t.TypeRegistry.Set('ValibotNaN', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TNaN = v.NanSchema> extends t.TSchema { +export interface TValibotNaN = v.NanSchema> extends t.TSchema { [t.Kind]: 'ValibotNaN' static: v.InferOutput type: Type } -function _NaN>(type: Type, options?: t.SchemaOptions): TNaN { +function _NaN>(type: Type, options?: t.SchemaOptions): TValibotNaN { return t.CreateType({ [t.Kind]: 'ValibotNaN', type }, options) as never } -type TFromNaN> = t.Ensure> +type TFromNaN> = t.Ensure> function FromNaN(type: BaseSchema): t.TSchema { return _NaN(type as v.NanSchema, Options(type)) } @@ -555,18 +555,18 @@ function FromPickList(type: BaseSchema): t.TSchema { // ------------------------------------------------------------------ // Promise // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotPromise', (schema, value) => { +t.TypeRegistry.Set('ValibotPromise', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TPromise = v.PromiseSchema> extends t.TSchema { +export interface TValibotPromise = v.PromiseSchema> extends t.TSchema { [t.Kind]: 'ValibotPromise' static: v.InferOutput type: Type } -function _Promise>(type: Type, options?: t.SchemaOptions): TPromise { +function _Promise>(type: Type, options?: t.SchemaOptions): TValibotPromise { return t.CreateType({ [t.Kind]: 'ValibotPromise', type }, options) as never } -type TFromPromise> = t.Ensure> +type TFromPromise> = t.Ensure> function FromPromise(type: BaseSchema): t.TSchema { return _Promise(type as v.PromiseSchema, Options(type)) } @@ -581,18 +581,18 @@ function FromRecord(type: BaseSchema) { // ------------------------------------------------------------------ // Set // ------------------------------------------------------------------ -t.TypeRegistry.Set('ValibotSet', (schema, value) => { +t.TypeRegistry.Set('ValibotSet', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TSet = v.SetSchema> extends t.TSchema { +export interface TValibotSet = v.SetSchema> extends t.TSchema { [t.Kind]: 'ValibotSet' static: v.InferOutput extends infer Result ? Result : never type: Type } -function Set>(type: Type, options?: t.SchemaOptions): TSet { +function Set>(type: Type, options?: t.SchemaOptions): TValibotSet { return t.CreateType({ [t.Kind]: 'ValibotSet', type }, options) as never } -type TFromSet> = t.Ensure> +type TFromSet> = t.Ensure> function FromSet(type: BaseSchema): t.TSchema { return Set(type as v.SetSchema) } @@ -738,7 +738,7 @@ function FromVoid(type: BaseSchema): t.TSchema { // Type // ------------------------------------------------------------------ // prettier-ignore -export type TFromType = ( +type TFromType = ( // Pipes - Extract First Type And Remap Type extends { pipe: [infer Type extends BaseSchema, ...any[]] } ? TFromType : // Types @@ -789,7 +789,7 @@ export type TFromType = ( t.TNever ) // prettier-ignore -export function FromType(type: Type): TFromType { +function FromType(type: Type): TFromType { return ( type.type === 'any' ? FromAny(type) : type.type === 'array' ? FromArray(type) : @@ -842,11 +842,12 @@ export function FromType(type: Type): TFromType { // TypeBoxFromValibot // ------------------------------------------------------------------ // prettier-ignore -export type TTypeBoxFromValibot = ( - Type extends BaseSchema ? TFromType : t.TNever -) +export type TTypeBoxFromValibot, + Result extends t.TSchema = TFromType +> = Result + /** Converts a Valibot Type to a TypeBox Type */ // prettier-ignore -export function TypeBoxFromValibot = TTypeBoxFromValibot>(type: Type): Result { +export function TypeBoxFromValibot, Result extends TTypeBoxFromValibot = TTypeBoxFromValibot>(type: Type): Result { return (Guard.IsValibot(type) ? FromType(type) : t.Never()) as never } diff --git a/src/typebox/typebox-from-zod.ts b/src/typebox/typebox-from-zod.ts index 1621c40..0a6db68 100644 --- a/src/typebox/typebox-from-zod.ts +++ b/src/typebox/typebox-from-zod.ts @@ -406,11 +406,10 @@ function FromType(type: Type): t.TSchema { // TypeBoxFromZod // ------------------------------------------------------------------ // prettier-ignore -export type TTypeBoxFromZod = ( - Type extends z.ZodType ? TFromType : t.TNever -) - +export type TTypeBoxFromZod, + Result extends t.TSchema = TFromType +> = Result // prettier-ignore -export function TypeBoxFromZod = TTypeBoxFromZod>(type: Type): Result { - return (type instanceof z.ZodType ? FromType(type) : t.Never()) as never +export function TypeBoxFromZod, Result extends TTypeBoxFromZod = TTypeBoxFromZod>(type: Type): Result { + return FromType(type) as never } diff --git a/src/typebox/typebox.ts b/src/typebox/typebox.ts index d3cf2e6..bc70fc4 100644 --- a/src/typebox/typebox.ts +++ b/src/typebox/typebox.ts @@ -67,10 +67,10 @@ export function ContextFromParameter(parameter: Pa /** Creates a TypeBox type from Syntax or another Type */ // prettier-ignore export type TTypeBox, Type> : - g.TIsTypeBox extends true ? TTypeBoxFromTypeBox : - g.TIsValibot extends true ? TTypeBoxFromValibot : - g.TIsZod extends true ? TTypeBoxFromZod : + Type extends g.SyntaxType ? TTypeBoxFromSyntax, Type> : + Type extends g.TypeBoxType ? TTypeBoxFromTypeBox : + Type extends g.ValibotType ? TTypeBoxFromValibot : + Type extends g.ZodType ? TTypeBoxFromZod : t.TNever )> = Result /** Creates a TypeBox type from Syntax or another Type */ @@ -82,7 +82,7 @@ export function TypeBox(type: Type, options?: TSyn export function TypeBox(...args: any[]): never { const [parameter, type, options] = g.Signature(args) return ( - t.ValueGuard.IsString(type) ? TypeBoxFromSyntax(ContextFromParameter(parameter), type, options) : + g.IsSyntax(type) ? TypeBoxFromSyntax(ContextFromParameter(parameter), type, options) : g.IsTypeBox(type) ? TypeBoxFromTypeBox(type) : g.IsValibot(type) ? TypeBoxFromValibot(type) : g.IsZod(type) ? TypeBoxFromZod(type) : diff --git a/src/valibot/valibot-from-syntax.ts b/src/valibot/valibot-from-syntax.ts index bc37fb6..ecdf7f6 100644 --- a/src/valibot/valibot-from-syntax.ts +++ b/src/valibot/valibot-from-syntax.ts @@ -30,7 +30,7 @@ import { TTypeBoxFromSyntax, TypeBoxFromSyntax } from '../typebox/typebox-from-s import { ValibotFromTypeBox, TValibotFromTypeBox } from './valibot-from-typebox' import * as t from '@sinclair/typebox' -import * as c from './common' +import * as v from 'valibot' // ------------------------------------------------------------------ // ValibotFromSyntax @@ -38,7 +38,7 @@ import * as c from './common' // prettier-ignore export type TValibotFromSyntax, - Result extends c.BaseSchema = TValibotFromTypeBox + Result extends v.BaseSchema = TValibotFromTypeBox > = Result // prettier-ignore diff --git a/src/valibot/valibot-from-typebox.ts b/src/valibot/valibot-from-typebox.ts index 4c18bf7..b38ae3f 100644 --- a/src/valibot/valibot-from-typebox.ts +++ b/src/valibot/valibot-from-typebox.ts @@ -183,20 +183,6 @@ function FromRecord(type: t.TRecord): c.BaseSchema { ) } // ------------------------------------------------------------------ -// Optional -// ------------------------------------------------------------------ -type TFromOptional, c.BaseError>> = Result -function FromOptional(type: t.TOptional): c.BaseSchema { - return v.optional(FromType(t.Optional(type, false))) -} -// ------------------------------------------------------------------ -// Readonly -// ------------------------------------------------------------------ -type TFromReadonly> = Result -function FromReadonly(type: t.TReadonly): c.BaseSchema { - return FromType(t.Readonly(type, false)) -} -// ------------------------------------------------------------------ // Never // ------------------------------------------------------------------ type TFromNever> = Result @@ -317,7 +303,11 @@ function FromVoid(type: t.TVoid): c.BaseSchema { // ------------------------------------------------------------------ // Types // ------------------------------------------------------------------ -type TFromTypes = Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] ? TFromTypes]> : Result +// prettier-ignore +type TFromTypes = + Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] + ? TFromTypes]> + : Result function FromTypes(types: t.TSchema[]): c.BaseSchema[] { return types.map((type) => FromType(type)) } @@ -325,39 +315,48 @@ function FromTypes(types: t.TSchema[]): c.BaseSchema[] { // Type // ------------------------------------------------------------------ // prettier-ignore -type TFromType = ( - Type extends t.TReadonly ? TFromReadonly : - Type extends t.TOptional ? TFromOptional : - Type extends t.TAny ? TFromAny : - Type extends t.TArray ? TFromArray : - Type extends t.TBigInt ? TFromBigInt : - Type extends t.TBoolean ? TFromBoolean : - Type extends t.TDate ? TFromDate : - Type extends t.TFunction ? TFromFunction : - Type extends t.TInteger ? TFromInteger : - Type extends t.TIntersect ? TFromIntersect : - Type extends t.TLiteral ? TFromLiteral : - Type extends t.TNull ? TFromNull : - Type extends t.TNever ? TFromNever : - Type extends t.TNumber ? TFromNumber : - Type extends t.TObject ? TFromObject : - Type extends t.TPromise ? TFromPromise : - Type extends t.TRecord ? TFromRecord : - Type extends t.TRegExp ? TFromRegExp : - Type extends t.TString ? TFromString : - Type extends t.TSymbol ? TFromSymbol : - Type extends t.TTuple ? TFromTuple : - Type extends t.TUndefined ? TFromUndefined : - Type extends t.TUnion ? TFromUnion : - Type extends t.TUnknown ? TFromUnknown : - Type extends t.TVoid ? TFromVoid : - v.NeverSchema -) +type TFromType = ( + Type extends t.TAny ? TFromAny : + Type extends t.TArray ? TFromArray : + Type extends t.TBigInt ? TFromBigInt : + Type extends t.TBoolean ? TFromBoolean : + Type extends t.TDate ? TFromDate : + Type extends t.TFunction ? TFromFunction : + Type extends t.TInteger ? TFromInteger : + Type extends t.TIntersect ? TFromIntersect : + Type extends t.TLiteral ? TFromLiteral : + Type extends t.TNull ? TFromNull : + Type extends t.TNever ? TFromNever : + Type extends t.TNumber ? TFromNumber : + Type extends t.TObject ? TFromObject : + Type extends t.TPromise ? TFromPromise : + Type extends t.TRecord ? TFromRecord : + Type extends t.TRegExp ? TFromRegExp : + Type extends t.TString ? TFromString : + Type extends t.TSymbol ? TFromSymbol : + Type extends t.TTuple ? TFromTuple : + Type extends t.TUndefined ? TFromUndefined : + Type extends t.TUnion ? TFromUnion : + Type extends t.TUnknown ? TFromUnknown : + Type extends t.TVoid ? TFromVoid : + v.NeverSchema + ), + // Modifier Mapping + IsReadonly extends boolean = Type extends t.TReadonly ? true : false, + IsOptional extends boolean = Type extends t.TOptional ? true : false, + Result extends v.BaseSchema = ( + [IsReadonly, IsOptional] extends [true, true] ? v.OptionalSchema : + [IsReadonly, IsOptional] extends [false, true] ? v.OptionalSchema : + [IsReadonly, IsOptional] extends [true, false] ? Mapped : + Mapped + ) +> = Result // prettier-ignore function FromType(type: t.TSchema): c.BaseSchema { - return ( - t.KindGuard.IsReadonly(type) ? FromReadonly(type) : - t.KindGuard.IsOptional(type) ? FromOptional(type) : + // Type Mapping + const mapped = ( t.KindGuard.IsAny(type) ? FromAny(type) : t.KindGuard.IsArray(type) ? FromArray(type) : t.KindGuard.IsBigInt(type) ? FromBigInt(type) : @@ -383,15 +382,25 @@ function FromType(type: t.TSchema): c.BaseSchema { t.KindGuard.IsVoid(type) ? FromVoid(type) : v.never() ) + // Modifier Mapping + const isOptional = t.KindGuard.IsOptional(type) + const isReadonly = t.KindGuard.IsReadonly(type) + const result = ( + isOptional && isReadonly ? v.optional(mapped) : + isOptional && !isReadonly ? v.optional(mapped) : + !isOptional && isReadonly ? mapped : + mapped + ) + return result } // ------------------------------------------------------------------ // ValibotFromTypeBox // ------------------------------------------------------------------ // prettier-ignore -export type TValibotFromTypeBox : v.NeverSchema -)> = Result +export type TValibotFromTypeBox +> = Result // prettier-ignore -export function ValibotFromTypeBox(type: Type): TValibotFromTypeBox { - return (t.KindGuard.IsSchema(type) ? FromType(type) : v.never()) as never +export function ValibotFromTypeBox(type: Type): TValibotFromTypeBox { + return FromType(type) as never } diff --git a/src/valibot/valibot-from-valibot.ts b/src/valibot/valibot-from-valibot.ts index 2e77ba2..179ed8b 100644 --- a/src/valibot/valibot-from-valibot.ts +++ b/src/valibot/valibot-from-valibot.ts @@ -26,15 +26,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as Guard from '../guard' import * as v from 'valibot' -import * as c from './common' + +// ------------------------------------------------------------------ +// ValibotFromValibot +// ------------------------------------------------------------------ // prettier-ignore -export type TValibotFromValibot +export type TValibotFromValibot, + Result extends v.BaseSchema = Type > = Result + // prettier-ignore -export function ValibotFromValibot(type: Type): TValibotFromValibot { - return (Guard.IsValibot(type) ? type : undefined) as never +export function ValibotFromValibot>(type: Type): TValibotFromValibot { + return type } diff --git a/src/valibot/valibot-from-zod.ts b/src/valibot/valibot-from-zod.ts index 3f79661..2dd9437 100644 --- a/src/valibot/valibot-from-zod.ts +++ b/src/valibot/valibot-from-zod.ts @@ -28,19 +28,25 @@ THE SOFTWARE. import { type TTypeBoxFromZod, TypeBoxFromZod } from '../typebox/typebox-from-zod' import { type TValibotFromTypeBox, ValibotFromTypeBox } from './valibot-from-typebox' + import * as t from '@sinclair/typebox' import * as v from 'valibot' +import * as z from 'zod' +// ------------------------------------------------------------------ +// ValibotFromZod +// ------------------------------------------------------------------ // prettier-ignore -export type TValibotFromZod< - Type extends object | string, - Schema extends t.TSchema = TTypeBoxFromZod, - Result extends v.BaseSchema = TValibotFromTypeBox +export type TValibotFromZod, + TypeBox extends t.TSchema = TTypeBoxFromZod, + Result extends v.BaseSchema = TValibotFromTypeBox > = Result // prettier-ignore -export function ValibotFromZod(type: Type): TValibotFromZod { +export function ValibotFromZod, + Result extends v.BaseSchema = TValibotFromZod +>(type: Type): Result { const schema = TypeBoxFromZod(type) const result = ValibotFromTypeBox(schema) - return result + return result as never } diff --git a/src/valibot/valibot.ts b/src/valibot/valibot.ts index 5430226..2a225d9 100644 --- a/src/valibot/valibot.ts +++ b/src/valibot/valibot.ts @@ -43,11 +43,11 @@ import { TParameter, TContextFromParameter, ContextFromParameter } from '../type // ------------------------------------------------------------------ /** Creates a Valibot type from Syntax or another Type */ // prettier-ignore -export type TValibot, Type> : - g.TIsTypeBox extends true ? TValibotFromTypeBox : - g.TIsValibot extends true ? TValibotFromValibot : - g.TIsZod extends true ? TValibotFromZod : +export type TValibot, Type> : + Type extends t.TSchema ? TValibotFromTypeBox : + Type extends g.ValibotType ? TValibotFromValibot : + Type extends g.ZodType ? TValibotFromZod : v.NeverSchema )> = Result @@ -58,14 +58,9 @@ export function Valibot(type: Type, options?: TSyntaxOption /** Creates a Valibot type from Syntax or another Type */ export function Valibot(type: Type, options?: TSyntaxOptions): TValibot<{}, Type> /** Creates a Valibot type from Syntax or another Type */ -// prettier-ignore export function Valibot(...args: any[]): never { const [parameter, type, options] = g.Signature(args) return ( - 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() + g.IsSyntax(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 } diff --git a/src/zod/zod-from-syntax.ts b/src/zod/zod-from-syntax.ts index fc0aceb..a5ed0d9 100644 --- a/src/zod/zod-from-syntax.ts +++ b/src/zod/zod-from-syntax.ts @@ -34,6 +34,7 @@ import * as z from 'zod' // ------------------------------------------------------------------ // ZodFromSyntax // ------------------------------------------------------------------ + // prettier-ignore export type TZodFromSyntax, diff --git a/src/zod/zod-from-typebox.ts b/src/zod/zod-from-typebox.ts index da1aa2d..f7c8c53 100644 --- a/src/zod/zod-from-typebox.ts +++ b/src/zod/zod-from-typebox.ts @@ -175,16 +175,30 @@ function FromRecord(type: t.TRecord): z.ZodTypeAny { // ------------------------------------------------------------------ // Optional // ------------------------------------------------------------------ -type TFromOptional>> = Result +// prettier-ignore +type TFromOptional, + NonOptional extends t.TSchema = t.TOptionalWithFlag, + Mapped extends z.ZodTypeAny | z.ZodEffects = TFromType, + Result = z.ZodOptional +> = Result function FromOptional(type: t.TOptional): z.ZodTypeAny { - return z.optional(FromType(t.Optional(type, false))) + const non_optional = t.Optional(type, false) + const mapped = FromType(non_optional) + return z.optional(mapped) } // ------------------------------------------------------------------ // Readonly // ------------------------------------------------------------------ -type TFromReadonly>> = Result +// prettier-ignore +type TFromReadonly, + NonReadonly extends t.TSchema = t.TReadonlyWithFlag, + Mapped extends z.ZodTypeAny | z.ZodEffects = TFromType, + Result = z.ZodReadonly +> = Result function FromReadonly(type: t.TReadonly): z.ZodTypeAny { - return FromType(t.Readonly(type, false)) + const non_readonly = t.Readonly(type, false) + const mapped = FromType(non_readonly) + return mapped // no mapping } // ------------------------------------------------------------------ // Never @@ -315,40 +329,50 @@ function FromTypes(types: t.TSchema[]): z.ZodTypeAny[] { // Type // ------------------------------------------------------------------ // prettier-ignore -type TFromType = ( - Type extends t.TReadonly ? TFromReadonly : - Type extends t.TOptional ? TFromOptional : - Type extends t.TAny ? TFromAny : - Type extends t.TArray ? TFromArray : - Type extends t.TBigInt ? TFromBigInt : - Type extends t.TBoolean ? TFromBoolean : - Type extends t.TDate ? TFromDate : - Type extends t.TFunction ? TFromFunction : - Type extends t.TInteger ? TFromInteger : - Type extends t.TIntersect ? TFromIntersect : - Type extends t.TLiteral ? TFromLiteral : - Type extends t.TNever ? TFromNever : - Type extends t.TNull ? TFromNull : - Type extends t.TNumber ? TFromNumber : - Type extends t.TObject ? TFromObject : - Type extends t.TPromise ? TFromPromise : - Type extends t.TRecord ? TFromRecord : - Type extends t.TRegExp ? TFromRegExp : - Type extends t.TString ? TFromString : - Type extends t.TSymbol ? TFromSymbol : - Type extends t.TTuple ? TFromTuple : - Type extends t.TUndefined ? TFromUndefined : - Type extends t.TUnion ? TFromUnion : - Type extends t.TUnknown ? TFromUnknown : - Type extends t.TVoid ? TFromVoid : - z.ZodNever -) +type TFromType = ( + Type extends t.TAny ? TFromAny : + Type extends t.TArray ? TFromArray : + Type extends t.TBigInt ? TFromBigInt : + Type extends t.TBoolean ? TFromBoolean : + Type extends t.TDate ? TFromDate : + Type extends t.TFunction ? TFromFunction : + Type extends t.TInteger ? TFromInteger : + Type extends t.TIntersect ? TFromIntersect : + Type extends t.TLiteral ? TFromLiteral : + Type extends t.TNever ? TFromNever : + Type extends t.TNull ? TFromNull : + Type extends t.TNumber ? TFromNumber : + Type extends t.TObject ? TFromObject : + Type extends t.TPromise ? TFromPromise : + Type extends t.TRecord ? TFromRecord : + Type extends t.TRegExp ? TFromRegExp : + Type extends t.TString ? TFromString : + Type extends t.TSymbol ? TFromSymbol : + Type extends t.TTuple ? TFromTuple : + Type extends t.TUndefined ? TFromUndefined : + Type extends t.TUnion ? TFromUnion : + Type extends t.TUnknown ? TFromUnknown : + Type extends t.TVoid ? TFromVoid : + z.ZodNever + ), + // Modifier Mapping + IsReadonly extends boolean = Type extends t.TReadonly ? true : false, + IsOptional extends boolean = Type extends t.TOptional ? true : false, + Result extends z.ZodTypeAny | z.ZodEffects = ( + [IsReadonly, IsOptional] extends [true, true] ? z.ZodReadonly> : + [IsReadonly, IsOptional] extends [false, true] ? z.ZodOptional : + [IsReadonly, IsOptional] extends [true, false] ? z.ZodReadonly : + Mapped + ) +> = Result // prettier-ignore -function FromType(type: t.TSchema): z.ZodTypeAny { +function FromType(type: t.TSchema): z.ZodTypeAny | z.ZodEffects { const constraints: TConstraint[] = [] if(!t.ValueGuard.IsUndefined(type.description)) constraints.push(input => input.describe(type.description!)) if(!t.ValueGuard.IsUndefined(type.default)) constraints.push(input => input.default(type.default)) - return constraints.reduce((type, constraint) => constraint(type), ( + const mapped = constraints.reduce((type, constraint) => constraint(type), ( t.KindGuard.IsReadonly(type) ? FromReadonly(type) : t.KindGuard.IsOptional(type) ? FromOptional(type) : t.KindGuard.IsAny(type) ? FromAny(type) : @@ -376,14 +400,24 @@ function FromType(type: t.TSchema): z.ZodTypeAny { t.KindGuard.IsVoid(type) ? FromVoid(type) : z.never() )) + // Modifier Mapping + const isOptional = t.KindGuard.IsOptional(type) + const isReadonly = t.KindGuard.IsReadonly(type) + const result = ( + isOptional && isReadonly ? z.optional(mapped) : + isOptional && !isReadonly ? z.optional(mapped) : + !isOptional && isReadonly ? mapped : + mapped + ) + return result } // ------------------------------------------------------------------ // ZodFromTypeBox // ------------------------------------------------------------------ // prettier-ignore -export type TZodFromTypeBox = ( - Type extends t.TSchema ? TFromType : z.ZodNever -) -export function ZodFromTypeBox(type: Type): TZodFromTypeBox { +export type TZodFromTypeBox = TFromType +> = Result +export function ZodFromTypeBox(type: Type): TZodFromTypeBox { return (t.KindGuard.IsSchema(type) ? FromType(type) : z.never()) as never } diff --git a/src/zod/zod-from-valibot.ts b/src/zod/zod-from-valibot.ts index eb91575..27d80bc 100644 --- a/src/zod/zod-from-valibot.ts +++ b/src/zod/zod-from-valibot.ts @@ -28,17 +28,19 @@ THE SOFTWARE. import { type TTypeBoxFromValibot, TypeBoxFromValibot } from '../typebox/typebox-from-valibot' import { type TZodFromTypeBox, ZodFromTypeBox } from './zod-from-typebox' + import * as t from '@sinclair/typebox' +import * as v from 'valibot' import * as z from 'zod' // prettier-ignore -export type TZodFromValibot, Schema extends t.TSchema = TTypeBoxFromValibot, Result extends z.ZodTypeAny | z.ZodEffects = TZodFromTypeBox > = Result // prettier-ignore -export function ZodFromValibot(type: Type): TZodFromValibot { +export function ZodFromValibot>(type: Type): TZodFromValibot { const schema = TypeBoxFromValibot(type) const result = ZodFromTypeBox(schema) return result diff --git a/src/zod/zod-from-zod.ts b/src/zod/zod-from-zod.ts index fda6fc3..3e85875 100644 --- a/src/zod/zod-from-zod.ts +++ b/src/zod/zod-from-zod.ts @@ -32,15 +32,11 @@ import * as z from 'zod' type BaseType = z.ZodTypeAny | z.ZodEffects // prettier-ignore -export type TZodFromZod = Result // prettier-ignore -export function ZodFromZod(type: Type): TZodFromZod { +export function ZodFromZod(type: Type): TZodFromZod { return (Guard.IsZod(type) ? type : z.never()) as never } diff --git a/src/zod/zod.ts b/src/zod/zod.ts index f6bb44b..017f5ed 100644 --- a/src/zod/zod.ts +++ b/src/zod/zod.ts @@ -33,7 +33,6 @@ import { type TZodFromZod, ZodFromZod } from './zod-from-zod' 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' @@ -43,11 +42,12 @@ import { TParameter, TContextFromParameter, ContextFromParameter } from '../type // ------------------------------------------------------------------ /** Creates a Zod type from Syntax or another Type */ // prettier-ignore -export type TZod, Type> : - g.TIsTypeBox extends true ? TZodFromTypeBox : - g.TIsValibot extends true ? TZodFromValibot : - g.TIsZod extends true ? TZodFromZod : +export type TZod = ( + Type extends g.SyntaxType ? TZodFromSyntax, Type> : + Type extends g.TypeBoxType ? TZodFromTypeBox : + // @ts-ignore + Type extends g.ValibotType ? TZodFromValibot : + Type extends g.ZodType ? TZodFromZod : z.ZodNever )> = Result @@ -58,14 +58,7 @@ export function Zod(type: Type, options?: TSyntaxOptions): /** Creates a Zod type from Syntax or another Type */ export function Zod(type: Type, options?: TSyntaxOptions): TZod<{}, Type> /** Creates a Zod type from Syntax or another Type */ -// prettier-ignore export function Zod(...args: any[]): never { const [parameter, type, options] = g.Signature(args) - return ( - 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 + return (g.IsSyntax(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 } diff --git a/typemap.png b/typemap.png index a53394b..11c1b66 100644 Binary files a/typemap.png and b/typemap.png differ