This commit is contained in:
sinclair
2025-12-24 15:44:34 +09:00
commit 13d553220c
1047 changed files with 80931 additions and 0 deletions

665
src/compiler/compiler.ts Normal file
View File

@@ -0,0 +1,665 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/compiler
The MIT License (MIT)
Copyright (c) 2017-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 { TransformEncode, TransformDecode, HasTransform, TransformDecodeCheckError, TransformEncodeCheckError } from '../value/transform/index'
import { Errors, ValueErrorIterator } from '../errors/index'
import { TypeSystemPolicy } from '../system/index'
import { TypeBoxError } from '../type/error/index'
import { Deref } from '../value/deref/index'
import { Hash } from '../value/hash/index'
import { Kind } from '../type/symbols/index'
import { TypeRegistry, FormatRegistry } from '../type/registry/index'
import { KeyOfPattern } from '../type/keyof/index'
import { ExtendsUndefinedCheck } from '../type/extends/extends-undefined'
import type { TSchema } from '../type/schema/index'
import type { TAsyncIterator } from '../type/async-iterator/index'
import type { TAny } from '../type/any/index'
import type { TArgument } from '../type/argument/index'
import type { TArray } from '../type/array/index'
import type { TBigInt } from '../type/bigint/index'
import type { TBoolean } from '../type/boolean/index'
import type { TDate } from '../type/date/index'
import type { TConstructor } from '../type/constructor/index'
import type { TFunction } from '../type/function/index'
import type { TImport } from '../type/module/index'
import type { TInteger } from '../type/integer/index'
import type { TIntersect } from '../type/intersect/index'
import type { TIterator } from '../type/iterator/index'
import type { TLiteral } from '../type/literal/index'
import { Never, type TNever } from '../type/never/index'
import type { TNot } from '../type/not/index'
import type { TNull } from '../type/null/index'
import type { TNumber } from '../type/number/index'
import type { TObject } from '../type/object/index'
import type { TPromise } from '../type/promise/index'
import type { TRecord } from '../type/record/index'
import { Ref, type TRef } from '../type/ref/index'
import type { TRegExp } from '../type/regexp/index'
import type { TTemplateLiteral } from '../type/template-literal/index'
import type { TThis } from '../type/recursive/index'
import type { TTuple } from '../type/tuple/index'
import type { TUnion } from '../type/union/index'
import type { TUnknown } from '../type/unknown/index'
import type { Static, StaticDecode, StaticEncode } from '../type/static/index'
import type { TString } from '../type/string/index'
import type { TSymbol } from '../type/symbol/index'
import type { TUndefined } from '../type/undefined/index'
import type { TUint8Array } from '../type/uint8array/index'
import type { TVoid } from '../type/void/index'
// ------------------------------------------------------------------
// ValueGuard
// ------------------------------------------------------------------
import { IsArray, IsString, IsNumber, IsBigInt } from '../value/guard/index'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsSchema } from '../type/guard/type'
// ------------------------------------------------------------------
// CheckFunction
// ------------------------------------------------------------------
export type CheckFunction = (value: unknown) => boolean
// ------------------------------------------------------------------
// TypeCheck
// ------------------------------------------------------------------
export class TypeCheck<T extends TSchema> {
private readonly hasTransform: boolean
constructor(private readonly schema: T, private readonly references: TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) {
this.hasTransform = HasTransform(schema, references)
}
/** Returns the generated assertion code used to validate this type. */
public Code(): string {
return this.code
}
/** Returns the schema type used to validate */
public Schema(): T {
return this.schema
}
/** Returns reference types used to validate */
public References(): TSchema[] {
return this.references
}
/** Returns an iterator for each error in this value. */
public Errors(value: unknown): ValueErrorIterator {
return Errors(this.schema, this.references, value)
}
/** Returns true if the value matches the compiled type. */
public Check(value: unknown): value is Static<T> {
return this.checkFunc(value)
}
/** Decodes a value or throws if error */
public Decode<Static = StaticDecode<T>, Result extends Static = Static>(value: unknown): Result {
if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!)
return (this.hasTransform ? TransformDecode(this.schema, this.references, value) : value) as never
}
/** Encodes a value or throws if error */
public Encode<Static = StaticEncode<T>, Result extends Static = Static>(value: unknown): Result {
const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value
if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!)
return encoded as never
}
}
// ------------------------------------------------------------------
// Character
// ------------------------------------------------------------------
namespace Character {
export function DollarSign(code: number) {
return code === 36
}
export function IsUnderscore(code: number) {
return code === 95
}
export function IsAlpha(code: number) {
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122)
}
export function IsNumeric(code: number) {
return code >= 48 && code <= 57
}
}
// ------------------------------------------------------------------
// MemberExpression
// ------------------------------------------------------------------
namespace MemberExpression {
function IsFirstCharacterNumeric(value: string) {
if (value.length === 0) return false
return Character.IsNumeric(value.charCodeAt(0))
}
function IsAccessor(value: string) {
if (IsFirstCharacterNumeric(value)) return false
for (let i = 0; i < value.length; i++) {
const code = value.charCodeAt(i)
const check = Character.IsAlpha(code) || Character.IsNumeric(code) || Character.DollarSign(code) || Character.IsUnderscore(code)
if (!check) return false
}
return true
}
function EscapeHyphen(key: string) {
return key.replace(/'/g, "\\'")
}
export function Encode(object: string, key: string) {
return IsAccessor(key) ? `${object}.${key}` : `${object}['${EscapeHyphen(key)}']`
}
}
// ------------------------------------------------------------------
// Identifier
// ------------------------------------------------------------------
namespace Identifier {
export function Encode($id: string) {
const buffer: string[] = []
for (let i = 0; i < $id.length; i++) {
const code = $id.charCodeAt(i)
if (Character.IsNumeric(code) || Character.IsAlpha(code)) {
buffer.push($id.charAt(i))
} else {
buffer.push(`_${code}_`)
}
}
return buffer.join('').replace(/__/g, '_')
}
}
// ------------------------------------------------------------------
// LiteralString
// ------------------------------------------------------------------
namespace LiteralString {
export function Escape(content: string) {
return content.replace(/'/g, "\\'")
}
}
// ------------------------------------------------------------------
// Errors
// ------------------------------------------------------------------
export class TypeCompilerUnknownTypeError extends TypeBoxError {
constructor(public readonly schema: TSchema) {
super('Unknown type')
}
}
export class TypeCompilerTypeGuardError extends TypeBoxError {
constructor(public readonly schema: TSchema) {
super('Preflight validation check failed to guard for the given schema')
}
}
// ------------------------------------------------------------------
// Policy
// ------------------------------------------------------------------
export namespace Policy {
export function IsExactOptionalProperty(value: string, key: string, expression: string) {
return TypeSystemPolicy.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)`
}
export function IsObjectLike(value: string): string {
return !TypeSystemPolicy.AllowArrayObject ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)`
}
export function IsRecordLike(value: string): string {
return !TypeSystemPolicy.AllowArrayObject
? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))`
: `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))`
}
export function IsNumberLike(value: string): string {
return TypeSystemPolicy.AllowNaN ? `typeof ${value} === 'number'` : `Number.isFinite(${value})`
}
export function IsVoidLike(value: string): string {
return TypeSystemPolicy.AllowNullVoid ? `(${value} === undefined || ${value} === null)` : `${value} === undefined`
}
}
// ------------------------------------------------------------------
// TypeCompiler
// ------------------------------------------------------------------
export type TypeCompilerLanguageOption = 'typescript' | 'javascript'
export interface TypeCompilerCodegenOptions {
language?: TypeCompilerLanguageOption
}
/** Compiles Types for Runtime Type Checking */
export namespace TypeCompiler {
// ----------------------------------------------------------------
// Guards
// ----------------------------------------------------------------
function IsAnyOrUnknown(schema: TSchema) {
return schema[Kind] === 'Any' || schema[Kind] === 'Unknown'
}
// ----------------------------------------------------------------
// Types
// ----------------------------------------------------------------
function* FromAny(schema: TAny, references: TSchema[], value: string): IterableIterator<string> {
yield 'true'
}
function* FromArgument(schema: TArgument, references: TSchema[], value: string): IterableIterator<string> {
yield 'true'
}
function* FromArray(schema: TArray, references: TSchema[], value: string): IterableIterator<string> {
yield `Array.isArray(${value})`
const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')]
if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}`
if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}`
const elementExpression = CreateExpression(schema.items, references, 'value')
yield `${value}.every((${parameter}) => ${elementExpression})`
if (IsSchema(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains)) {
const containsSchema = IsSchema(schema.contains) ? schema.contains : Never()
const checkExpression = CreateExpression(containsSchema, references, 'value')
const checkMinContains = IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : []
const checkMaxContains = IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : []
const checkCount = `const count = value.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)`
const check = [`(count > 0)`, ...checkMinContains, ...checkMaxContains].join(' && ')
yield `((${parameter}) => { ${checkCount}; return ${check}})(${value})`
}
if (schema.uniqueItems === true) {
const check = `const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true`
const block = `const set = new Set(); for(const element of value) { ${check} }`
yield `((${parameter}) => { ${block} )(${value})`
}
}
function* FromAsyncIterator(schema: TAsyncIterator, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof value === 'object' && Symbol.asyncIterator in ${value})`
}
function* FromBigInt(schema: TBigInt, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof ${value} === 'bigint')`
if (IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})`
if (IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})`
if (IsBigInt(schema.maximum)) yield `${value} <= BigInt(${schema.maximum})`
if (IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})`
if (IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0`
}
function* FromBoolean(schema: TBoolean, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof ${value} === 'boolean')`
}
function* FromConstructor(schema: TConstructor, references: TSchema[], value: string): IterableIterator<string> {
yield* Visit(schema.returns, references, `${value}.prototype`)
}
function* FromDate(schema: TDate, references: TSchema[], value: string): IterableIterator<string> {
yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())`
if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}`
if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}`
if (IsNumber(schema.maximumTimestamp)) yield `${value}.getTime() <= ${schema.maximumTimestamp}`
if (IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}`
if (IsNumber(schema.multipleOfTimestamp)) yield `(${value}.getTime() % ${schema.multipleOfTimestamp}) === 0`
}
function* FromFunction(schema: TFunction, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof ${value} === 'function')`
}
function* FromImport(schema: TImport, references: TSchema[], value: string): IterableIterator<string> {
const members = globalThis.Object.getOwnPropertyNames(schema.$defs).reduce((result, key) => {
return [...result, schema.$defs[key as never] as TSchema]
}, [] as TSchema[])
yield* Visit(Ref(schema.$ref), [...references, ...members], value)
}
function* FromInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator<string> {
yield `Number.isInteger(${value})`
if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}`
if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}`
if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}`
if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}`
if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0`
}
function* FromIntersect(schema: TIntersect, references: TSchema[], value: string): IterableIterator<string> {
const check1 = schema.allOf.map((schema: TSchema) => CreateExpression(schema, references, value)).join(' && ')
if (schema.unevaluatedProperties === false) {
const keyCheck = CreateVariable(`${new RegExp(KeyOfPattern(schema))};`)
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))`
yield `(${check1} && ${check2})`
} else if (IsSchema(schema.unevaluatedProperties)) {
const keyCheck = CreateVariable(`${new RegExp(KeyOfPattern(schema))};`)
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})`
yield `(${check1} && ${check2})`
} else {
yield `(${check1})`
}
}
function* FromIterator(schema: TIterator, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof value === 'object' && Symbol.iterator in ${value})`
}
function* FromLiteral(schema: TLiteral, references: TSchema[], value: string): IterableIterator<string> {
if (typeof schema.const === 'number' || typeof schema.const === 'boolean') {
yield `(${value} === ${schema.const})`
} else {
yield `(${value} === '${LiteralString.Escape(schema.const)}')`
}
}
function* FromNever(schema: TNever, references: TSchema[], value: string): IterableIterator<string> {
yield `false`
}
function* FromNot(schema: TNot, references: TSchema[], value: string): IterableIterator<string> {
const expression = CreateExpression(schema.not, references, value)
yield `(!${expression})`
}
function* FromNull(schema: TNull, references: TSchema[], value: string): IterableIterator<string> {
yield `(${value} === null)`
}
function* FromNumber(schema: TNumber, references: TSchema[], value: string): IterableIterator<string> {
yield Policy.IsNumberLike(value)
if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}`
if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}`
if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}`
if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}`
if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0`
}
function* FromObject(schema: TObject, references: TSchema[], value: string): IterableIterator<string> {
yield Policy.IsObjectLike(value)
if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}`
if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}`
const knownKeys = Object.getOwnPropertyNames(schema.properties)
for (const knownKey of knownKeys) {
const memberExpression = MemberExpression.Encode(value, knownKey)
const property = schema.properties[knownKey]
if (schema.required && schema.required.includes(knownKey)) {
yield* Visit(property, references, memberExpression)
if (ExtendsUndefinedCheck(property) || IsAnyOrUnknown(property)) yield `('${knownKey}' in ${value})`
} else {
const expression = CreateExpression(property, references, memberExpression)
yield Policy.IsExactOptionalProperty(value, knownKey, expression)
}
}
if (schema.additionalProperties === false) {
if (schema.required && schema.required.length === knownKeys.length) {
yield `Object.getOwnPropertyNames(${value}).length === ${knownKeys.length}`
} else {
const keys = `[${knownKeys.map((key) => `'${key}'`).join(', ')}]`
yield `Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key))`
}
}
if (typeof schema.additionalProperties === 'object') {
const expression = CreateExpression(schema.additionalProperties, references, `${value}[key]`)
const keys = `[${knownKeys.map((key) => `'${key}'`).join(', ')}]`
yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))`
}
}
function* FromPromise(schema: TPromise, references: TSchema[], value: string): IterableIterator<string> {
yield `${value} instanceof Promise`
}
function* FromRecord(schema: TRecord, references: TSchema[], value: string): IterableIterator<string> {
yield Policy.IsRecordLike(value)
if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}`
if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}`
const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0]
const variable = CreateVariable(`${new RegExp(patternKey)}`)
const check1 = CreateExpression(patternSchema, references, 'value')
const check2 = IsSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true'
const expression = `(${variable}.test(key) ? ${check1} : ${check2})`
yield `(Object.entries(${value}).every(([key, value]) => ${expression}))`
}
function* FromRef(schema: TRef, references: TSchema[], value: string): IterableIterator<string> {
const target = Deref(schema, references)
// Reference: If we have seen this reference before we can just yield and return the function call.
// If this isn't the case we defer to visit to generate and set the function for subsequent passes.
if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})`
yield* Visit(target, references, value)
}
function* FromRegExp(schema: TRegExp, references: TSchema[], value: string): IterableIterator<string> {
const variable = CreateVariable(`${new RegExp(schema.source, schema.flags)};`)
yield `(typeof ${value} === 'string')`
if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}`
if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}`
yield `${variable}.test(${value})`
}
function* FromString(schema: TString, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof ${value} === 'string')`
if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}`
if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}`
if (schema.pattern !== undefined) {
const variable = CreateVariable(`${new RegExp(schema.pattern)};`)
yield `${variable}.test(${value})`
}
if (schema.format !== undefined) {
yield `format('${schema.format}', ${value})`
}
}
function* FromSymbol(schema: TSymbol, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof ${value} === 'symbol')`
}
function* FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[], value: string): IterableIterator<string> {
yield `(typeof ${value} === 'string')`
const variable = CreateVariable(`${new RegExp(schema.pattern)};`)
yield `${variable}.test(${value})`
}
function* FromThis(schema: TThis, references: TSchema[], value: string): IterableIterator<string> {
// Note: This types are assured to be hoisted prior to this call. Just yield the function.
yield `${CreateFunctionName(schema.$ref)}(${value})`
}
function* FromTuple(schema: TTuple, references: TSchema[], value: string): IterableIterator<string> {
yield `Array.isArray(${value})`
if (schema.items === undefined) return yield `${value}.length === 0`
yield `(${value}.length === ${schema.maxItems})`
for (let i = 0; i < schema.items.length; i++) {
const expression = CreateExpression(schema.items[i], references, `${value}[${i}]`)
yield `${expression}`
}
}
function* FromUndefined(schema: TUndefined, references: TSchema[], value: string): IterableIterator<string> {
yield `${value} === undefined`
}
function* FromUnion(schema: TUnion, references: TSchema[], value: string): IterableIterator<string> {
const expressions = schema.anyOf.map((schema: TSchema) => CreateExpression(schema, references, value))
yield `(${expressions.join(' || ')})`
}
function* FromUint8Array(schema: TUint8Array, references: TSchema[], value: string): IterableIterator<string> {
yield `${value} instanceof Uint8Array`
if (IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})`
if (IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})`
}
function* FromUnknown(schema: TUnknown, references: TSchema[], value: string): IterableIterator<string> {
yield 'true'
}
function* FromVoid(schema: TVoid, references: TSchema[], value: string): IterableIterator<string> {
yield Policy.IsVoidLike(value)
}
function* FromKind(schema: TSchema, references: TSchema[], value: string): IterableIterator<string> {
const instance = state.instances.size
state.instances.set(instance, schema)
yield `kind('${schema[Kind]}', ${instance}, ${value})`
}
function* Visit(schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): IterableIterator<string> {
const references_ = IsString(schema.$id) ? [...references, schema] : references
const schema_ = schema as any
// --------------------------------------------------------------
// Hoisting
// --------------------------------------------------------------
if (useHoisting && IsString(schema.$id)) {
const functionName = CreateFunctionName(schema.$id)
if (state.functions.has(functionName)) {
return yield `${functionName}(${value})`
} else {
// Note: In the case of cyclic types, we need to create a 'functions' record
// to prevent infinitely re-visiting the CreateFunction. Subsequent attempts
// to visit will be caught by the above condition.
state.functions.set(functionName, '<deferred>')
const functionCode = CreateFunction(functionName, schema, references, 'value', false)
state.functions.set(functionName, functionCode)
return yield `${functionName}(${value})`
}
}
switch (schema_[Kind]) {
case 'Any':
return yield* FromAny(schema_, references_, value)
case 'Argument':
return yield* FromArgument(schema_, references_, value)
case 'Array':
return yield* FromArray(schema_, references_, value)
case 'AsyncIterator':
return yield* FromAsyncIterator(schema_, references_, value)
case 'BigInt':
return yield* FromBigInt(schema_, references_, value)
case 'Boolean':
return yield* FromBoolean(schema_, references_, value)
case 'Constructor':
return yield* FromConstructor(schema_, references_, value)
case 'Date':
return yield* FromDate(schema_, references_, value)
case 'Function':
return yield* FromFunction(schema_, references_, value)
case 'Import':
return yield* FromImport(schema_, references_, value)
case 'Integer':
return yield* FromInteger(schema_, references_, value)
case 'Intersect':
return yield* FromIntersect(schema_, references_, value)
case 'Iterator':
return yield* FromIterator(schema_, references_, value)
case 'Literal':
return yield* FromLiteral(schema_, references_, value)
case 'Never':
return yield* FromNever(schema_, references_, value)
case 'Not':
return yield* FromNot(schema_, references_, value)
case 'Null':
return yield* FromNull(schema_, references_, value)
case 'Number':
return yield* FromNumber(schema_, references_, value)
case 'Object':
return yield* FromObject(schema_, references_, value)
case 'Promise':
return yield* FromPromise(schema_, references_, value)
case 'Record':
return yield* FromRecord(schema_, references_, value)
case 'Ref':
return yield* FromRef(schema_, references_, value)
case 'RegExp':
return yield* FromRegExp(schema_, references_, value)
case 'String':
return yield* FromString(schema_, references_, value)
case 'Symbol':
return yield* FromSymbol(schema_, references_, value)
case 'TemplateLiteral':
return yield* FromTemplateLiteral(schema_, references_, value)
case 'This':
return yield* FromThis(schema_, references_, value)
case 'Tuple':
return yield* FromTuple(schema_, references_, value)
case 'Undefined':
return yield* FromUndefined(schema_, references_, value)
case 'Union':
return yield* FromUnion(schema_, references_, value)
case 'Uint8Array':
return yield* FromUint8Array(schema_, references_, value)
case 'Unknown':
return yield* FromUnknown(schema_, references_, value)
case 'Void':
return yield* FromVoid(schema_, references_, value)
default:
if (!TypeRegistry.Has(schema_[Kind])) throw new TypeCompilerUnknownTypeError(schema)
return yield* FromKind(schema_, references_, value)
}
}
// ----------------------------------------------------------------
// Compiler State
// ----------------------------------------------------------------
// prettier-ignore
const state = {
language: 'javascript', // target language
functions: new Map<string, string>(), // local functions
variables: new Map<string, string>(), // local variables
instances: new Map<number, TSchema>() // exterior kind instances
}
// ----------------------------------------------------------------
// Compiler Factory
// ----------------------------------------------------------------
function CreateExpression(schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): string {
return `(${[...Visit(schema, references, value, useHoisting)].join(' && ')})`
}
function CreateFunctionName($id: string) {
return `check_${Identifier.Encode($id)}`
}
function CreateVariable(expression: string) {
const variableName = `local_${state.variables.size}`
state.variables.set(variableName, `const ${variableName} = ${expression}`)
return variableName
}
function CreateFunction(name: string, schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): string {
const [newline, pad] = ['\n', (length: number) => ''.padStart(length, ' ')]
const parameter = CreateParameter('value', 'any')
const returns = CreateReturns('boolean')
const expression = [...Visit(schema, references, value, useHoisting)].map((expression) => `${pad(4)}${expression}`).join(` &&${newline}`)
return `function ${name}(${parameter})${returns} {${newline}${pad(2)}return (${newline}${expression}${newline}${pad(2)})\n}`
}
function CreateParameter(name: string, type: string) {
const annotation = state.language === 'typescript' ? `: ${type}` : ''
return `${name}${annotation}`
}
function CreateReturns(type: string) {
return state.language === 'typescript' ? `: ${type}` : ''
}
// ----------------------------------------------------------------
// Compile
// ----------------------------------------------------------------
function Build<T extends TSchema>(schema: T, references: TSchema[], options: TypeCompilerCodegenOptions): string {
const functionCode = CreateFunction('check', schema, references, 'value') // will populate functions and variables
const parameter = CreateParameter('value', 'any')
const returns = CreateReturns('boolean')
const functions = [...state.functions.values()]
const variables = [...state.variables.values()]
// prettier-ignore
const checkFunction = IsString(schema.$id) // ensure top level schemas with $id's are hoisted
? `return function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}`
: `return ${functionCode}`
return [...variables, ...functions, checkFunction].join('\n')
}
/** Generates the code used to assert this type and returns it as a string */
export function Code<T extends TSchema>(schema: T, references: TSchema[], options?: TypeCompilerCodegenOptions): string
/** Generates the code used to assert this type and returns it as a string */
export function Code<T extends TSchema>(schema: T, options?: TypeCompilerCodegenOptions): string
/** Generates the code used to assert this type and returns it as a string */
export function Code(...args: any[]) {
const defaults = { language: 'javascript' }
// prettier-ignore
const [schema, references, options] = (
args.length === 2 && IsArray(args[1]) ? [args[0], args[1], defaults] :
args.length === 2 && !IsArray(args[1]) ? [args[0], [], args[1]] :
args.length === 3 ? [args[0], args[1], args[2]] :
args.length === 1 ? [args[0], [], defaults] :
[null, [], defaults]
)
// compiler-reset
state.language = options.language
state.variables.clear()
state.functions.clear()
state.instances.clear()
if (!IsSchema(schema)) throw new TypeCompilerTypeGuardError(schema)
for (const schema of references) if (!IsSchema(schema)) throw new TypeCompilerTypeGuardError(schema)
return Build(schema, references, options)
}
/** Compiles a TypeBox type for optimal runtime type checking. Types must be valid TypeBox types of TSchema */
export function Compile<T extends TSchema>(schema: T, references: TSchema[] = []): TypeCheck<T> {
const generatedCode = Code(schema, references, { language: 'javascript' })
const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode)
const instances = new Map(state.instances)
function typeRegistryFunction(kind: string, instance: number, value: unknown) {
if (!TypeRegistry.Has(kind) || !instances.has(instance)) return false
const checkFunc = TypeRegistry.Get(kind)!
const schema = instances.get(instance)!
return checkFunc(schema, value)
}
function formatRegistryFunction(format: string, value: string) {
if (!FormatRegistry.Has(format)) return false
const checkFunc = FormatRegistry.Get(format)!
return checkFunc(value)
}
function hashFunction(value: unknown) {
return Hash(value)
}
const checkFunction = compiledFunction(typeRegistryFunction, formatRegistryFunction, hashFunction)
return new TypeCheck(schema, references, checkFunction, generatedCode)
}
}

30
src/compiler/index.ts Normal file
View File

@@ -0,0 +1,30 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/compiler
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index'
export * from './compiler'

639
src/errors/errors.ts Normal file
View File

@@ -0,0 +1,639 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/errors
The MIT License (MIT)
Copyright (c) 2017-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 { TypeSystemPolicy } from '../system/index'
import { KeyOfPattern } from '../type/keyof/index'
import { TypeRegistry, FormatRegistry } from '../type/registry/index'
import { ExtendsUndefinedCheck } from '../type/extends/extends-undefined'
import { GetErrorFunction } from './function'
import { TypeBoxError } from '../type/error/index'
import { Deref } from '../value/deref/index'
import { Hash } from '../value/hash/index'
import { Check } from '../value/check/index'
import { Kind } from '../type/symbols/index'
import type { TSchema } from '../type/schema/index'
import type { TAsyncIterator } from '../type/async-iterator/index'
import type { TAny } from '../type/any/index'
import type { TArray } from '../type/array/index'
import type { TBigInt } from '../type/bigint/index'
import type { TBoolean } from '../type/boolean/index'
import type { TDate } from '../type/date/index'
import type { TConstructor } from '../type/constructor/index'
import type { TFunction } from '../type/function/index'
import type { TImport } from '../type/module/index'
import type { TInteger } from '../type/integer/index'
import type { TIntersect } from '../type/intersect/index'
import type { TIterator } from '../type/iterator/index'
import type { TLiteral } from '../type/literal/index'
import { Never, type TNever } from '../type/never/index'
import type { TNot } from '../type/not/index'
import type { TNull } from '../type/null/index'
import type { TNumber } from '../type/number/index'
import type { TObject } from '../type/object/index'
import type { TPromise } from '../type/promise/index'
import type { TRecord } from '../type/record/index'
import type { TRef } from '../type/ref/index'
import type { TRegExp } from '../type/regexp/index'
import type { TTemplateLiteral } from '../type/template-literal/index'
import type { TThis } from '../type/recursive/index'
import type { TTuple } from '../type/tuple/index'
import type { TUnion } from '../type/union/index'
import type { TUnknown } from '../type/unknown/index'
import type { TString } from '../type/string/index'
import type { TSymbol } from '../type/symbol/index'
import type { TUndefined } from '../type/undefined/index'
import type { TUint8Array } from '../type/uint8array/index'
import type { TVoid } from '../type/void/index'
// ------------------------------------------------------------------
// ValueGuard
// ------------------------------------------------------------------
// prettier-ignore
import {
IsArray,
IsUint8Array,
IsDate,
IsPromise,
IsFunction,
IsAsyncIterator,
IsIterator,
IsBoolean,
IsNumber,
IsBigInt,
IsString,
IsSymbol,
IsInteger,
IsNull,
IsUndefined
} from '../value/guard/index'
// ------------------------------------------------------------------
// ValueErrorType
// ------------------------------------------------------------------
export enum ValueErrorType {
ArrayContains,
ArrayMaxContains,
ArrayMaxItems,
ArrayMinContains,
ArrayMinItems,
ArrayUniqueItems,
Array,
AsyncIterator,
BigIntExclusiveMaximum,
BigIntExclusiveMinimum,
BigIntMaximum,
BigIntMinimum,
BigIntMultipleOf,
BigInt,
Boolean,
DateExclusiveMaximumTimestamp,
DateExclusiveMinimumTimestamp,
DateMaximumTimestamp,
DateMinimumTimestamp,
DateMultipleOfTimestamp,
Date,
Function,
IntegerExclusiveMaximum,
IntegerExclusiveMinimum,
IntegerMaximum,
IntegerMinimum,
IntegerMultipleOf,
Integer,
IntersectUnevaluatedProperties,
Intersect,
Iterator,
Kind,
Literal,
Never,
Not,
Null,
NumberExclusiveMaximum,
NumberExclusiveMinimum,
NumberMaximum,
NumberMinimum,
NumberMultipleOf,
Number,
ObjectAdditionalProperties,
ObjectMaxProperties,
ObjectMinProperties,
ObjectRequiredProperty,
Object,
Promise,
RegExp,
StringFormatUnknown,
StringFormat,
StringMaxLength,
StringMinLength,
StringPattern,
String,
Symbol,
TupleLength,
Tuple,
Uint8ArrayMaxByteLength,
Uint8ArrayMinByteLength,
Uint8Array,
Undefined,
Union,
Void,
}
// ------------------------------------------------------------------
// ValueError
// ------------------------------------------------------------------
export interface ValueError {
type: ValueErrorType
schema: TSchema
path: string
value: unknown
message: string
errors: ValueErrorIterator[]
}
// ------------------------------------------------------------------
// ValueErrors
// ------------------------------------------------------------------
export class ValueErrorsUnknownTypeError extends TypeBoxError {
constructor(public readonly schema: TSchema) {
super('Unknown type')
}
}
// ------------------------------------------------------------------
// EscapeKey
// ------------------------------------------------------------------
function EscapeKey(key: string): string {
return key.replace(/~/g, '~0').replace(/\//g, '~1') // RFC6901 Path
}
// ------------------------------------------------------------------
// Guards
// ------------------------------------------------------------------
function IsDefined<T>(value: unknown): value is T {
return value !== undefined
}
// ------------------------------------------------------------------
// ValueErrorIterator
// ------------------------------------------------------------------
export class ValueErrorIterator {
constructor(private readonly iterator: IterableIterator<ValueError>) {}
public [Symbol.iterator]() {
return this.iterator
}
/** Returns the first value error or undefined if no errors */
public First(): ValueError | undefined {
const next = this.iterator.next()
return next.done ? undefined : next.value
}
}
// --------------------------------------------------------------------------
// Create
// --------------------------------------------------------------------------
function Create(errorType: ValueErrorType, schema: TSchema, path: string, value: unknown, errors: ValueErrorIterator[] = []): ValueError {
return {
type: errorType,
schema,
path,
value,
message: GetErrorFunction()({ errorType, path, schema, value, errors }),
errors,
}
}
// --------------------------------------------------------------------------
// Types
// --------------------------------------------------------------------------
function* FromAny(schema: TAny, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {}
function* FromArgument(schema: TAny, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {}
function* FromArray(schema: TArray, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsArray(value)) {
return yield Create(ValueErrorType.Array, schema, path, value)
}
if (IsDefined<number>(schema.minItems) && !(value.length >= schema.minItems)) {
yield Create(ValueErrorType.ArrayMinItems, schema, path, value)
}
if (IsDefined<number>(schema.maxItems) && !(value.length <= schema.maxItems)) {
yield Create(ValueErrorType.ArrayMaxItems, schema, path, value)
}
for (let i = 0; i < value.length; i++) {
yield* Visit(schema.items, references, `${path}/${i}`, value[i])
}
// prettier-ignore
if (schema.uniqueItems === true && !((function () { const set = new Set(); for (const element of value) { const hashed = Hash(element); if (set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) {
yield Create(ValueErrorType.ArrayUniqueItems, schema, path, value)
}
// contains
if (!(IsDefined(schema.contains) || IsDefined(schema.minContains) || IsDefined(schema.maxContains))) {
return
}
const containsSchema = IsDefined<TSchema>(schema.contains) ? schema.contains : Never()
const containsCount = value.reduce((acc: number, value, index) => (Visit(containsSchema, references, `${path}${index}`, value).next().done === true ? acc + 1 : acc), 0)
if (containsCount === 0) {
yield Create(ValueErrorType.ArrayContains, schema, path, value)
}
if (IsNumber(schema.minContains) && containsCount < schema.minContains) {
yield Create(ValueErrorType.ArrayMinContains, schema, path, value)
}
if (IsNumber(schema.maxContains) && containsCount > schema.maxContains) {
yield Create(ValueErrorType.ArrayMaxContains, schema, path, value)
}
}
function* FromAsyncIterator(schema: TAsyncIterator, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsAsyncIterator(value)) yield Create(ValueErrorType.AsyncIterator, schema, path, value)
}
function* FromBigInt(schema: TBigInt, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsBigInt(value)) return yield Create(ValueErrorType.BigInt, schema, path, value)
if (IsDefined<bigint>(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
yield Create(ValueErrorType.BigIntExclusiveMaximum, schema, path, value)
}
if (IsDefined<bigint>(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
yield Create(ValueErrorType.BigIntExclusiveMinimum, schema, path, value)
}
if (IsDefined<bigint>(schema.maximum) && !(value <= schema.maximum)) {
yield Create(ValueErrorType.BigIntMaximum, schema, path, value)
}
if (IsDefined<bigint>(schema.minimum) && !(value >= schema.minimum)) {
yield Create(ValueErrorType.BigIntMinimum, schema, path, value)
}
if (IsDefined<bigint>(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) {
yield Create(ValueErrorType.BigIntMultipleOf, schema, path, value)
}
}
function* FromBoolean(schema: TBoolean, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsBoolean(value)) yield Create(ValueErrorType.Boolean, schema, path, value)
}
function* FromConstructor(schema: TConstructor, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
yield* Visit(schema.returns, references, path, value.prototype)
}
function* FromDate(schema: TDate, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsDate(value)) return yield Create(ValueErrorType.Date, schema, path, value)
if (IsDefined<number>(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) {
yield Create(ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value)
}
if (IsDefined<number>(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) {
yield Create(ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value)
}
if (IsDefined<number>(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) {
yield Create(ValueErrorType.DateMaximumTimestamp, schema, path, value)
}
if (IsDefined<number>(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) {
yield Create(ValueErrorType.DateMinimumTimestamp, schema, path, value)
}
if (IsDefined<number>(schema.multipleOfTimestamp) && !(value.getTime() % schema.multipleOfTimestamp === 0)) {
yield Create(ValueErrorType.DateMultipleOfTimestamp, schema, path, value)
}
}
function* FromFunction(schema: TFunction, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsFunction(value)) yield Create(ValueErrorType.Function, schema, path, value)
}
function* FromImport(schema: TImport, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
const target = schema.$defs[schema.$ref] as TSchema
yield* Visit(target, [...references, ...definitions], path, value)
}
function* FromInteger(schema: TInteger, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsInteger(value)) return yield Create(ValueErrorType.Integer, schema, path, value)
if (IsDefined<number>(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
yield Create(ValueErrorType.IntegerExclusiveMaximum, schema, path, value)
}
if (IsDefined<number>(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
yield Create(ValueErrorType.IntegerExclusiveMinimum, schema, path, value)
}
if (IsDefined<number>(schema.maximum) && !(value <= schema.maximum)) {
yield Create(ValueErrorType.IntegerMaximum, schema, path, value)
}
if (IsDefined<number>(schema.minimum) && !(value >= schema.minimum)) {
yield Create(ValueErrorType.IntegerMinimum, schema, path, value)
}
if (IsDefined<number>(schema.multipleOf) && !(value % schema.multipleOf === 0)) {
yield Create(ValueErrorType.IntegerMultipleOf, schema, path, value)
}
}
function* FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
let hasError = false
for (const inner of schema.allOf) {
for (const error of Visit(inner, references, path, value)) {
hasError = true
yield error
}
}
if (hasError) {
return yield Create(ValueErrorType.Intersect, schema, path, value)
}
if (schema.unevaluatedProperties === false) {
const keyCheck = new RegExp(KeyOfPattern(schema))
for (const valueKey of Object.getOwnPropertyNames(value)) {
if (!keyCheck.test(valueKey)) {
yield Create(ValueErrorType.IntersectUnevaluatedProperties, schema, `${path}/${valueKey}`, value)
}
}
}
if (typeof schema.unevaluatedProperties === 'object') {
const keyCheck = new RegExp(KeyOfPattern(schema))
for (const valueKey of Object.getOwnPropertyNames(value)) {
if (!keyCheck.test(valueKey)) {
const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next()
if (!next.done) yield next.value // yield interior
}
}
}
}
function* FromIterator(schema: TIterator, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsIterator(value)) yield Create(ValueErrorType.Iterator, schema, path, value)
}
function* FromLiteral(schema: TLiteral, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!(value === schema.const)) yield Create(ValueErrorType.Literal, schema, path, value)
}
function* FromNever(schema: TNever, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
yield Create(ValueErrorType.Never, schema, path, value)
}
function* FromNot(schema: TNot, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (Visit(schema.not, references, path, value).next().done === true) yield Create(ValueErrorType.Not, schema, path, value)
}
function* FromNull(schema: TNull, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsNull(value)) yield Create(ValueErrorType.Null, schema, path, value)
}
function* FromNumber(schema: TNumber, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!TypeSystemPolicy.IsNumberLike(value)) return yield Create(ValueErrorType.Number, schema, path, value)
if (IsDefined<number>(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
yield Create(ValueErrorType.NumberExclusiveMaximum, schema, path, value)
}
if (IsDefined<number>(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
yield Create(ValueErrorType.NumberExclusiveMinimum, schema, path, value)
}
if (IsDefined<number>(schema.maximum) && !(value <= schema.maximum)) {
yield Create(ValueErrorType.NumberMaximum, schema, path, value)
}
if (IsDefined<number>(schema.minimum) && !(value >= schema.minimum)) {
yield Create(ValueErrorType.NumberMinimum, schema, path, value)
}
if (IsDefined<number>(schema.multipleOf) && !(value % schema.multipleOf === 0)) {
yield Create(ValueErrorType.NumberMultipleOf, schema, path, value)
}
}
function* FromObject(schema: TObject, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!TypeSystemPolicy.IsObjectLike(value)) return yield Create(ValueErrorType.Object, schema, path, value)
if (IsDefined<number>(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) {
yield Create(ValueErrorType.ObjectMinProperties, schema, path, value)
}
if (IsDefined<number>(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) {
yield Create(ValueErrorType.ObjectMaxProperties, schema, path, value)
}
const requiredKeys = Array.isArray(schema.required) ? schema.required : ([] as string[])
const knownKeys = Object.getOwnPropertyNames(schema.properties)
const unknownKeys = Object.getOwnPropertyNames(value)
for (const requiredKey of requiredKeys) {
if (unknownKeys.includes(requiredKey)) continue
yield Create(ValueErrorType.ObjectRequiredProperty, schema.properties[requiredKey], `${path}/${EscapeKey(requiredKey)}`, undefined)
}
if (schema.additionalProperties === false) {
for (const valueKey of unknownKeys) {
if (!knownKeys.includes(valueKey)) {
yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${EscapeKey(valueKey)}`, value[valueKey])
}
}
}
if (typeof schema.additionalProperties === 'object') {
for (const valueKey of unknownKeys) {
if (knownKeys.includes(valueKey)) continue
yield* Visit(schema.additionalProperties as TSchema, references, `${path}/${EscapeKey(valueKey)}`, value[valueKey])
}
}
for (const knownKey of knownKeys) {
const property = schema.properties[knownKey]
if (schema.required && schema.required.includes(knownKey)) {
yield* Visit(property, references, `${path}/${EscapeKey(knownKey)}`, value[knownKey])
if (ExtendsUndefinedCheck(schema) && !(knownKey in value)) {
yield Create(ValueErrorType.ObjectRequiredProperty, property, `${path}/${EscapeKey(knownKey)}`, undefined)
}
} else {
if (TypeSystemPolicy.IsExactOptionalProperty(value, knownKey)) {
yield* Visit(property, references, `${path}/${EscapeKey(knownKey)}`, value[knownKey])
}
}
}
}
function* FromPromise(schema: TPromise, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsPromise(value)) yield Create(ValueErrorType.Promise, schema, path, value)
}
function* FromRecord(schema: TRecord, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!TypeSystemPolicy.IsRecordLike(value)) return yield Create(ValueErrorType.Object, schema, path, value)
if (IsDefined<number>(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) {
yield Create(ValueErrorType.ObjectMinProperties, schema, path, value)
}
if (IsDefined<number>(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) {
yield Create(ValueErrorType.ObjectMaxProperties, schema, path, value)
}
const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0]
const regex = new RegExp(patternKey)
for (const [propertyKey, propertyValue] of Object.entries(value)) {
if (regex.test(propertyKey)) yield* Visit(patternSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue)
}
if (typeof schema.additionalProperties === 'object') {
for (const [propertyKey, propertyValue] of Object.entries(value)) {
if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as TSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue)
}
}
if (schema.additionalProperties === false) {
for (const [propertyKey, propertyValue] of Object.entries(value)) {
if (regex.test(propertyKey)) continue
return yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${EscapeKey(propertyKey)}`, propertyValue)
}
}
}
function* FromRef(schema: TRef, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
yield* Visit(Deref(schema, references), references, path, value)
}
function* FromRegExp(schema: TRegExp, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value)
if (IsDefined<number>(schema.minLength) && !(value.length >= schema.minLength)) {
yield Create(ValueErrorType.StringMinLength, schema, path, value)
}
if (IsDefined<number>(schema.maxLength) && !(value.length <= schema.maxLength)) {
yield Create(ValueErrorType.StringMaxLength, schema, path, value)
}
const regex = new RegExp(schema.source, schema.flags)
if (!regex.test(value)) {
return yield Create(ValueErrorType.RegExp, schema, path, value)
}
}
function* FromString(schema: TString, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value)
if (IsDefined<number>(schema.minLength) && !(value.length >= schema.minLength)) {
yield Create(ValueErrorType.StringMinLength, schema, path, value)
}
if (IsDefined<number>(schema.maxLength) && !(value.length <= schema.maxLength)) {
yield Create(ValueErrorType.StringMaxLength, schema, path, value)
}
if (IsString(schema.pattern)) {
const regex = new RegExp(schema.pattern)
if (!regex.test(value)) {
yield Create(ValueErrorType.StringPattern, schema, path, value)
}
}
if (IsString(schema.format)) {
if (!FormatRegistry.Has(schema.format)) {
yield Create(ValueErrorType.StringFormatUnknown, schema, path, value)
} else {
const format = FormatRegistry.Get(schema.format)!
if (!format(value)) {
yield Create(ValueErrorType.StringFormat, schema, path, value)
}
}
}
}
function* FromSymbol(schema: TSymbol, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsSymbol(value)) yield Create(ValueErrorType.Symbol, schema, path, value)
}
function* FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value)
const regex = new RegExp(schema.pattern)
if (!regex.test(value)) {
yield Create(ValueErrorType.StringPattern, schema, path, value)
}
}
function* FromThis(schema: TThis, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
yield* Visit(Deref(schema, references), references, path, value)
}
function* FromTuple(schema: TTuple<any[]>, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsArray(value)) return yield Create(ValueErrorType.Tuple, schema, path, value)
if (schema.items === undefined && !(value.length === 0)) {
return yield Create(ValueErrorType.TupleLength, schema, path, value)
}
if (!(value.length === schema.maxItems)) {
return yield Create(ValueErrorType.TupleLength, schema, path, value)
}
if (!schema.items) {
return
}
for (let i = 0; i < schema.items.length; i++) {
yield* Visit(schema.items[i], references, `${path}/${i}`, value[i])
}
}
function* FromUndefined(schema: TUndefined, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsUndefined(value)) yield Create(ValueErrorType.Undefined, schema, path, value)
}
function* FromUnion(schema: TUnion, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (Check(schema, references, value)) return
const errors = schema.anyOf.map((variant) => new ValueErrorIterator(Visit(variant, references, path, value)))
yield Create(ValueErrorType.Union, schema, path, value, errors)
}
function* FromUint8Array(schema: TUint8Array, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!IsUint8Array(value)) return yield Create(ValueErrorType.Uint8Array, schema, path, value)
if (IsDefined<number>(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) {
yield Create(ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value)
}
if (IsDefined<number>(schema.minByteLength) && !(value.length >= schema.minByteLength)) {
yield Create(ValueErrorType.Uint8ArrayMinByteLength, schema, path, value)
}
}
function* FromUnknown(schema: TUnknown, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {}
function* FromVoid(schema: TVoid, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
if (!TypeSystemPolicy.IsVoidLike(value)) yield Create(ValueErrorType.Void, schema, path, value)
}
function* FromKind(schema: TSchema, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
const check = TypeRegistry.Get(schema[Kind])!
if (!check(schema, value)) yield Create(ValueErrorType.Kind, schema, path, value)
}
function* Visit<T extends TSchema>(schema: T, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
const references_ = IsDefined<string>(schema.$id) ? [...references, schema] : references
const schema_ = schema as any
switch (schema_[Kind]) {
case 'Any':
return yield* FromAny(schema_, references_, path, value)
case 'Argument':
return yield* FromArgument(schema_, references_, path, value)
case 'Array':
return yield* FromArray(schema_, references_, path, value)
case 'AsyncIterator':
return yield* FromAsyncIterator(schema_, references_, path, value)
case 'BigInt':
return yield* FromBigInt(schema_, references_, path, value)
case 'Boolean':
return yield* FromBoolean(schema_, references_, path, value)
case 'Constructor':
return yield* FromConstructor(schema_, references_, path, value)
case 'Date':
return yield* FromDate(schema_, references_, path, value)
case 'Function':
return yield* FromFunction(schema_, references_, path, value)
case 'Import':
return yield* FromImport(schema_, references_, path, value)
case 'Integer':
return yield* FromInteger(schema_, references_, path, value)
case 'Intersect':
return yield* FromIntersect(schema_, references_, path, value)
case 'Iterator':
return yield* FromIterator(schema_, references_, path, value)
case 'Literal':
return yield* FromLiteral(schema_, references_, path, value)
case 'Never':
return yield* FromNever(schema_, references_, path, value)
case 'Not':
return yield* FromNot(schema_, references_, path, value)
case 'Null':
return yield* FromNull(schema_, references_, path, value)
case 'Number':
return yield* FromNumber(schema_, references_, path, value)
case 'Object':
return yield* FromObject(schema_, references_, path, value)
case 'Promise':
return yield* FromPromise(schema_, references_, path, value)
case 'Record':
return yield* FromRecord(schema_, references_, path, value)
case 'Ref':
return yield* FromRef(schema_, references_, path, value)
case 'RegExp':
return yield* FromRegExp(schema_, references_, path, value)
case 'String':
return yield* FromString(schema_, references_, path, value)
case 'Symbol':
return yield* FromSymbol(schema_, references_, path, value)
case 'TemplateLiteral':
return yield* FromTemplateLiteral(schema_, references_, path, value)
case 'This':
return yield* FromThis(schema_, references_, path, value)
case 'Tuple':
return yield* FromTuple(schema_, references_, path, value)
case 'Undefined':
return yield* FromUndefined(schema_, references_, path, value)
case 'Union':
return yield* FromUnion(schema_, references_, path, value)
case 'Uint8Array':
return yield* FromUint8Array(schema_, references_, path, value)
case 'Unknown':
return yield* FromUnknown(schema_, references_, path, value)
case 'Void':
return yield* FromVoid(schema_, references_, path, value)
default:
if (!TypeRegistry.Has(schema_[Kind])) throw new ValueErrorsUnknownTypeError(schema)
return yield* FromKind(schema_, references_, path, value)
}
}
/** Returns an iterator for each error in this value. */
export function Errors<T extends TSchema>(schema: T, references: TSchema[], value: unknown): ValueErrorIterator
/** Returns an iterator for each error in this value. */
export function Errors<T extends TSchema>(schema: T, value: unknown): ValueErrorIterator
/** Returns an iterator for each error in this value. */
export function Errors(...args: any[]) {
const iterator = args.length === 3 ? Visit(args[0], args[1], '', args[2]) : Visit(args[0], [], '', args[1])
return new ValueErrorIterator(iterator)
}

195
src/errors/function.ts Normal file
View File

@@ -0,0 +1,195 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/system
The MIT License (MIT)
Copyright (c) 2017-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 { TSchema } from '../type/schema/index'
import { Kind } from '../type/symbols/index'
import { ValueErrorIterator, ValueErrorType } from './errors'
/** Creates an error message using en-US as the default locale */
export function DefaultErrorFunction(error: ErrorFunctionParameter) {
switch (error.errorType) {
case ValueErrorType.ArrayContains:
return 'Expected array to contain at least one matching value'
case ValueErrorType.ArrayMaxContains:
return `Expected array to contain no more than ${error.schema.maxContains} matching values`
case ValueErrorType.ArrayMinContains:
return `Expected array to contain at least ${error.schema.minContains} matching values`
case ValueErrorType.ArrayMaxItems:
return `Expected array length to be less or equal to ${error.schema.maxItems}`
case ValueErrorType.ArrayMinItems:
return `Expected array length to be greater or equal to ${error.schema.minItems}`
case ValueErrorType.ArrayUniqueItems:
return 'Expected array elements to be unique'
case ValueErrorType.Array:
return 'Expected array'
case ValueErrorType.AsyncIterator:
return 'Expected AsyncIterator'
case ValueErrorType.BigIntExclusiveMaximum:
return `Expected bigint to be less than ${error.schema.exclusiveMaximum}`
case ValueErrorType.BigIntExclusiveMinimum:
return `Expected bigint to be greater than ${error.schema.exclusiveMinimum}`
case ValueErrorType.BigIntMaximum:
return `Expected bigint to be less or equal to ${error.schema.maximum}`
case ValueErrorType.BigIntMinimum:
return `Expected bigint to be greater or equal to ${error.schema.minimum}`
case ValueErrorType.BigIntMultipleOf:
return `Expected bigint to be a multiple of ${error.schema.multipleOf}`
case ValueErrorType.BigInt:
return 'Expected bigint'
case ValueErrorType.Boolean:
return 'Expected boolean'
case ValueErrorType.DateExclusiveMinimumTimestamp:
return `Expected Date timestamp to be greater than ${error.schema.exclusiveMinimumTimestamp}`
case ValueErrorType.DateExclusiveMaximumTimestamp:
return `Expected Date timestamp to be less than ${error.schema.exclusiveMaximumTimestamp}`
case ValueErrorType.DateMinimumTimestamp:
return `Expected Date timestamp to be greater or equal to ${error.schema.minimumTimestamp}`
case ValueErrorType.DateMaximumTimestamp:
return `Expected Date timestamp to be less or equal to ${error.schema.maximumTimestamp}`
case ValueErrorType.DateMultipleOfTimestamp:
return `Expected Date timestamp to be a multiple of ${error.schema.multipleOfTimestamp}`
case ValueErrorType.Date:
return 'Expected Date'
case ValueErrorType.Function:
return 'Expected function'
case ValueErrorType.IntegerExclusiveMaximum:
return `Expected integer to be less than ${error.schema.exclusiveMaximum}`
case ValueErrorType.IntegerExclusiveMinimum:
return `Expected integer to be greater than ${error.schema.exclusiveMinimum}`
case ValueErrorType.IntegerMaximum:
return `Expected integer to be less or equal to ${error.schema.maximum}`
case ValueErrorType.IntegerMinimum:
return `Expected integer to be greater or equal to ${error.schema.minimum}`
case ValueErrorType.IntegerMultipleOf:
return `Expected integer to be a multiple of ${error.schema.multipleOf}`
case ValueErrorType.Integer:
return 'Expected integer'
case ValueErrorType.IntersectUnevaluatedProperties:
return 'Unexpected property'
case ValueErrorType.Intersect:
return 'Expected all values to match'
case ValueErrorType.Iterator:
return 'Expected Iterator'
case ValueErrorType.Literal:
return `Expected ${typeof error.schema.const === 'string' ? `'${error.schema.const}'` : error.schema.const}`
case ValueErrorType.Never:
return 'Never'
case ValueErrorType.Not:
return 'Value should not match'
case ValueErrorType.Null:
return 'Expected null'
case ValueErrorType.NumberExclusiveMaximum:
return `Expected number to be less than ${error.schema.exclusiveMaximum}`
case ValueErrorType.NumberExclusiveMinimum:
return `Expected number to be greater than ${error.schema.exclusiveMinimum}`
case ValueErrorType.NumberMaximum:
return `Expected number to be less or equal to ${error.schema.maximum}`
case ValueErrorType.NumberMinimum:
return `Expected number to be greater or equal to ${error.schema.minimum}`
case ValueErrorType.NumberMultipleOf:
return `Expected number to be a multiple of ${error.schema.multipleOf}`
case ValueErrorType.Number:
return 'Expected number'
case ValueErrorType.Object:
return 'Expected object'
case ValueErrorType.ObjectAdditionalProperties:
return 'Unexpected property'
case ValueErrorType.ObjectMaxProperties:
return `Expected object to have no more than ${error.schema.maxProperties} properties`
case ValueErrorType.ObjectMinProperties:
return `Expected object to have at least ${error.schema.minProperties} properties`
case ValueErrorType.ObjectRequiredProperty:
return 'Expected required property'
case ValueErrorType.Promise:
return 'Expected Promise'
case ValueErrorType.RegExp:
return 'Expected string to match regular expression'
case ValueErrorType.StringFormatUnknown:
return `Unknown format '${error.schema.format}'`
case ValueErrorType.StringFormat:
return `Expected string to match '${error.schema.format}' format`
case ValueErrorType.StringMaxLength:
return `Expected string length less or equal to ${error.schema.maxLength}`
case ValueErrorType.StringMinLength:
return `Expected string length greater or equal to ${error.schema.minLength}`
case ValueErrorType.StringPattern:
return `Expected string to match '${error.schema.pattern}'`
case ValueErrorType.String:
return 'Expected string'
case ValueErrorType.Symbol:
return 'Expected symbol'
case ValueErrorType.TupleLength:
return `Expected tuple to have ${error.schema.maxItems || 0} elements`
case ValueErrorType.Tuple:
return 'Expected tuple'
case ValueErrorType.Uint8ArrayMaxByteLength:
return `Expected byte length less or equal to ${error.schema.maxByteLength}`
case ValueErrorType.Uint8ArrayMinByteLength:
return `Expected byte length greater or equal to ${error.schema.minByteLength}`
case ValueErrorType.Uint8Array:
return 'Expected Uint8Array'
case ValueErrorType.Undefined:
return 'Expected undefined'
case ValueErrorType.Union:
return 'Expected union value'
case ValueErrorType.Void:
return 'Expected void'
case ValueErrorType.Kind:
return `Expected kind '${error.schema[Kind]}'`
default:
return 'Unknown error type'
}
}
// ------------------------------------------------------------------
// ErrorFunction
// ------------------------------------------------------------------
export type ErrorFunctionParameter = {
/** The type of validation error */
errorType: ValueErrorType
/** The path of the error */
path: string
/** The schema associated with the error */
schema: TSchema
/** The value associated with the error */
value: unknown
/** Interior errors for this error */
errors: ValueErrorIterator[]
}
export type ErrorFunction = (parameter: ErrorFunctionParameter) => string
/** Manages error message providers */
let errorFunction: ErrorFunction = DefaultErrorFunction
/** Sets the error function used to generate error messages. */
export function SetErrorFunction(callback: ErrorFunction) {
errorFunction = callback
}
/** Gets the error function used to generate error messages */
export function GetErrorFunction(): ErrorFunction {
return errorFunction
}

30
src/errors/index.ts Normal file
View File

@@ -0,0 +1,30 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/errors
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './errors'
export * from './function'

109
src/index.ts Normal file
View File

@@ -0,0 +1,109 @@
/*--------------------------------------------------------------------------
@sinclair/typebox
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
// ------------------------------------------------------------------
// Infrastructure
// ------------------------------------------------------------------
export * from './type/clone/index'
export * from './type/create/index'
export * from './type/error/index'
export * from './type/guard/index'
export * from './type/helpers/index'
export * from './type/patterns/index'
export * from './type/registry/index'
export * from './type/sets/index'
export * from './type/symbols/index'
// ------------------------------------------------------------------
// Types
// ------------------------------------------------------------------
export * from './type/any/index'
export * from './type/array/index'
export * from './type/argument/index'
export * from './type/async-iterator/index'
export * from './type/awaited/index'
export * from './type/bigint/index'
export * from './type/boolean/index'
export * from './type/composite/index'
export * from './type/const/index'
export * from './type/constructor/index'
export * from './type/constructor-parameters/index'
export * from './type/date/index'
export * from './type/enum/index'
export * from './type/exclude/index'
export * from './type/extends/index'
export * from './type/extract/index'
export * from './type/function/index'
export * from './type/indexed/index'
export * from './type/instance-type/index'
export * from './type/instantiate/index'
export * from './type/integer/index'
export * from './type/intersect/index'
export * from './type/iterator/index'
export * from './type/intrinsic/index'
export * from './type/keyof/index'
export * from './type/literal/index'
export * from './type/module/index'
export * from './type/mapped/index'
export * from './type/never/index'
export * from './type/not/index'
export * from './type/null/index'
export * from './type/number/index'
export * from './type/object/index'
export * from './type/omit/index'
export * from './type/optional/index'
export * from './type/parameters/index'
export * from './type/partial/index'
export * from './type/pick/index'
export * from './type/promise/index'
export * from './type/readonly/index'
export * from './type/readonly-optional/index'
export * from './type/record/index'
export * from './type/recursive/index'
export * from './type/ref/index'
export * from './type/regexp/index'
export * from './type/required/index'
export * from './type/rest/index'
export * from './type/return-type/index'
export * from './type/schema/index'
export * from './type/static/index'
export * from './type/string/index'
export * from './type/symbol/index'
export * from './type/template-literal/index'
export * from './type/transform/index'
export * from './type/tuple/index'
export * from './type/uint8array/index'
export * from './type/undefined/index'
export * from './type/union/index'
export * from './type/unknown/index'
export * from './type/unsafe/index'
export * from './type/void/index'
// ------------------------------------------------------------------
// Type.*
// ------------------------------------------------------------------
export * from './type/type/index'

30
src/parser/index.ts Normal file
View File

@@ -0,0 +1,30 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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.
---------------------------------------------------------------------------*/
export * as Runtime from './runtime/index'
export * as Static from './static/index'

104
src/parser/runtime/guard.ts Normal file
View File

@@ -0,0 +1,104 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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 { IArray, IConst, IContext, IIdent, INumber, IOptional, IRef, IString, ITuple, IUnion } from './types'
// ------------------------------------------------------------------
// Value Guard
// ------------------------------------------------------------------
// prettier-ignore
function HasPropertyKey<Key extends PropertyKey>(value: Record<PropertyKey, unknown>, key: Key): value is Record<PropertyKey, unknown> & { [_ in Key]: unknown } {
return key in value
}
// prettier-ignore
function IsObjectValue(value: unknown): value is Record<PropertyKey, unknown> {
return typeof value === 'object' && value !== null
}
// prettier-ignore
function IsArrayValue(value: unknown): value is unknown[] {
return globalThis.Array.isArray(value)
}
// ------------------------------------------------------------------
// Parser Guard
// ------------------------------------------------------------------
/** Returns true if the value is a Array Parser */
export function IsArray(value: unknown): value is IArray {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Array' && HasPropertyKey(value, 'parser') && IsObjectValue(value.parser)
}
/** Returns true if the value is a Const Parser */
export function IsConst(value: unknown): value is IConst {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Const' && HasPropertyKey(value, 'value') && typeof value.value === 'string'
}
/** Returns true if the value is a Context Parser */
export function IsContext(value: unknown): value is IContext {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Context' && HasPropertyKey(value, 'left') && IsParser(value.left) && HasPropertyKey(value, 'right') && IsParser(value.right)
}
/** Returns true if the value is a Ident Parser */
export function IsIdent(value: unknown): value is IIdent {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ident'
}
/** Returns true if the value is a Number Parser */
export function IsNumber(value: unknown): value is INumber {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Number'
}
/** Returns true if the value is a Optional Parser */
export function IsOptional(value: unknown): value is IOptional {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Optional' && HasPropertyKey(value, 'parser') && IsObjectValue(value.parser)
}
/** Returns true if the value is a Ref Parser */
export function IsRef(value: unknown): value is IRef {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ref' && HasPropertyKey(value, 'ref') && typeof value.ref === 'string'
}
/** Returns true if the value is a String Parser */
export function IsString(value: unknown): value is IString {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'String' && HasPropertyKey(value, 'options') && IsArrayValue(value.options)
}
/** Returns true if the value is a Tuple Parser */
export function IsTuple(value: unknown): value is ITuple {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Tuple' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers)
}
/** Returns true if the value is a Union Parser */
export function IsUnion(value: unknown): value is IUnion {
return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Union' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers)
}
/** Returns true if the value is a Parser */
export function IsParser(value: unknown) {
// prettier-ignore
return (
IsArray(value) ||
IsConst(value) ||
IsContext(value) ||
IsIdent(value) ||
IsNumber(value) ||
IsOptional(value) ||
IsRef(value) ||
IsString(value) ||
IsTuple(value) ||
IsUnion(value)
)
}

View File

@@ -0,0 +1,33 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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.
---------------------------------------------------------------------------*/
export * as Guard from './guard'
export * as Token from './token'
export * from './types'
export * from './module'
export * from './parse'

View File

@@ -0,0 +1,52 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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 Types from './types'
import { Parse } from './parse'
// ------------------------------------------------------------------
// Module
// ------------------------------------------------------------------
export class Module<Properties extends Types.IModuleProperties = Types.IModuleProperties> {
constructor(private readonly properties: Properties) {}
/** Parses using one of the parsers defined on this instance */
public Parse<Key extends keyof Properties>(key: Key, content: string, context: unknown): [] | [Types.StaticParser<Properties[Key]>, string]
/** Parses using one of the parsers defined on this instance */
public Parse<Key extends keyof Properties>(key: Key, content: string): [] | [Types.StaticParser<Properties[Key]>, string]
/** Parses using one of the parsers defined on this instance */
public Parse(...args: any[]): never {
// prettier-ignore
const [key, content, context] = (
args.length === 3 ? [args[0], args[1], args[2]] :
args.length === 2 ? [args[0], args[1], undefined] :
(() => { throw Error('Invalid parse arguments') })()
)
return Parse(this.properties, this.properties[key], content, context) as never
}
}

179
src/parser/runtime/parse.ts Normal file
View File

@@ -0,0 +1,179 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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 Guard from './guard'
import * as Token from './token'
import * as Types from './types'
// ------------------------------------------------------------------
// Context
// ------------------------------------------------------------------
function ParseContext<ModuleProperties extends Types.IModuleProperties, Parser extends Types.IParser>(moduleProperties: ModuleProperties, left: Parser, right: Parser, code: string, context: unknown): unknown[] {
const result = ParseParser(moduleProperties, left, code, context)
return result.length === 2 ? ParseParser(moduleProperties, right, result[1], result[0]) : []
}
// ------------------------------------------------------------------
// Array
// ------------------------------------------------------------------
function ParseArray<ModuleProperties extends Types.IModuleProperties, Parser extends Types.IParser>(moduleProperties: ModuleProperties, parser: Parser, code: string, context: unknown): unknown[] {
const buffer = [] as unknown[]
let rest = code
while (rest.length > 0) {
const result = ParseParser(moduleProperties, parser, rest, context)
if (result.length === 0) return [buffer, rest]
buffer.push(result[0])
rest = result[1]
}
return [buffer, rest]
}
// ------------------------------------------------------------------
// Const
// ------------------------------------------------------------------
function ParseConst<Value extends string>(value: Value, code: string, context: unknown): [] | [Value, string] {
return Token.Const(value, code) as never
}
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
function ParseIdent(code: string, _context: unknown): [] | [string, string] {
return Token.Ident(code)
}
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
// prettier-ignore
function ParseNumber(code: string, _context: unknown): [] | [string, string] {
return Token.Number(code)
}
// ------------------------------------------------------------------
// Optional
// ------------------------------------------------------------------
function ParseOptional<ModuleProperties extends Types.IModuleProperties, Parser extends Types.IParser>(moduleProperties: ModuleProperties, parser: Parser, code: string, context: unknown): [] | [[unknown] | [], unknown] {
const result = ParseParser(moduleProperties, parser, code, context)
return (result.length === 2 ? [[result[0]], result[1]] : [[], code]) as never
}
// ------------------------------------------------------------------
// Ref
// ------------------------------------------------------------------
function ParseRef<ModuleProperties extends Types.IModuleProperties, Ref extends string>(moduleProperties: ModuleProperties, ref: Ref, code: string, context: unknown): [] | [string, string] {
const parser = moduleProperties[ref]
if (!Guard.IsParser(parser)) throw Error(`Cannot dereference Parser '${ref}'`)
return ParseParser(moduleProperties, parser, code, context) as never
}
// ------------------------------------------------------------------
// String
// ------------------------------------------------------------------
// prettier-ignore
function ParseString(options: string[], code: string, _context: unknown): [] | [string, string] {
return Token.String(options, code)
}
// ------------------------------------------------------------------
// Tuple
// ------------------------------------------------------------------
function ParseTuple<ModuleProperties extends Types.IModuleProperties, Parsers extends Types.IParser[]>(moduleProperties: ModuleProperties, parsers: [...Parsers], code: string, context: unknown): [] | [unknown[], string] {
const buffer = [] as unknown[]
let rest = code
for (const parser of parsers) {
const result = ParseParser(moduleProperties, parser, rest, context)
if (result.length === 0) return []
buffer.push(result[0])
rest = result[1]
}
return [buffer, rest]
}
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
// prettier-ignore
function ParseUnion<ModuleProperties extends Types.IModuleProperties, Parsers extends Types.IParser[]>(moduleProperties: ModuleProperties, parsers: [...Parsers], code: string, context: unknown): [] | [unknown, string] {
for(const parser of parsers) {
const result = ParseParser(moduleProperties, parser, code, context)
if(result.length === 0) continue
return result
}
return []
}
// ------------------------------------------------------------------
// Parser
// ------------------------------------------------------------------
// prettier-ignore
function ParseParser<Parser extends Types.IParser>(moduleProperties: Types.IModuleProperties, parser: Parser, code: string, context: unknown): [] | [Types.StaticParser<Parser>, string] {
const result = (
Guard.IsContext(parser) ? ParseContext(moduleProperties, parser.left, parser.right, code, context) :
Guard.IsArray(parser) ? ParseArray(moduleProperties, parser.parser, code, context) :
Guard.IsConst(parser) ? ParseConst(parser.value, code, context) :
Guard.IsIdent(parser) ? ParseIdent(code, context) :
Guard.IsNumber(parser) ? ParseNumber(code, context) :
Guard.IsOptional(parser) ? ParseOptional(moduleProperties, parser.parser, code, context) :
Guard.IsRef(parser) ? ParseRef(moduleProperties, parser.ref, code, context) :
Guard.IsString(parser) ? ParseString(parser.options, code, context) :
Guard.IsTuple(parser) ? ParseTuple(moduleProperties, parser.parsers, code, context) :
Guard.IsUnion(parser) ? ParseUnion(moduleProperties, parser.parsers, code, context) :
[]
)
return (
result.length === 2
? [parser.mapping(result[0], context), result[1]]
: result
) as never
}
// ------------------------------------------------------------------
// Parse
// ------------------------------------------------------------------
/** Parses content using the given Parser */
// prettier-ignore
export function Parse<Parser extends Types.IParser>(moduleProperties: Types.IModuleProperties, parser: Parser, code: string, context: unknown): [] | [Types.StaticParser<Parser>, string]
/** Parses content using the given Parser */
// prettier-ignore
export function Parse<Parser extends Types.IParser>(moduleProperties: Types.IModuleProperties, parser: Parser, code: string): [] | [Types.StaticParser<Parser>, string]
/** Parses content using the given Parser */
// prettier-ignore
export function Parse<Parser extends Types.IParser>(parser: Parser, content: string, context: unknown): [] | [Types.StaticParser<Parser>, string]
/** Parses content using the given Parser */
// prettier-ignore
export function Parse<Parser extends Types.IParser>(parser: Parser, content: string): [] | [Types.StaticParser<Parser>, string]
/** Parses content using the given parser */
// prettier-ignore
export function Parse(...args: any[]): never {
const withModuleProperties = typeof args[1] === 'string' ? false : true
const [moduleProperties, parser, content, context] = withModuleProperties
? [args[0], args[1], args[2], args[3]]
: [{}, args[0], args[1], args[2]]
return ParseParser(moduleProperties, parser, content, context) as never
}

247
src/parser/runtime/token.ts Normal file
View File

@@ -0,0 +1,247 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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.
---------------------------------------------------------------------------*/
// ------------------------------------------------------------------
// Chars
// ------------------------------------------------------------------
// prettier-ignore
namespace Chars {
/** Returns true if the char code is a whitespace */
export function IsWhitespace(value: number): boolean {
return value === 32
}
/** Returns true if the char code is a newline */
export function IsNewline(value: number): boolean {
return value === 10
}
/** Returns true if the char code is a alpha */
export function IsAlpha(value: number): boolean {
return (
(value >= 65 && value <= 90) || // A-Z
(value >= 97 && value <= 122) // a-z
)
}
/** Returns true if the char code is zero */
export function IsZero(value: number): boolean {
return value === 48
}
/** Returns true if the char code is non-zero */
export function IsNonZero(value: number): boolean {
return value >= 49 && value <= 57
}
/** Returns true if the char code is a digit */
export function IsDigit(value: number): boolean {
return (
IsNonZero(value) ||
IsZero(value)
)
}
/** Returns true if the char code is a dot */
export function IsDot(value: number): boolean {
return value === 46
}
/** Returns true if this char code is a underscore */
export function IsUnderscore(value: unknown): boolean {
return value === 95
}
/** Returns true if this char code is a dollar sign */
export function IsDollarSign(value: unknown): boolean {
return value === 36
}
}
// ------------------------------------------------------------------
// Trim
// ------------------------------------------------------------------
// prettier-ignore
namespace Trim {
/** Trims Whitespace and retains Newline, Tabspaces, etc. */
export function TrimWhitespaceOnly(code: string): string {
for (let i = 0; i < code.length; i++) {
if (Chars.IsWhitespace(code.charCodeAt(i))) continue
return code.slice(i)
}
return code
}
/** Trims Whitespace including Newline, Tabspaces, etc. */
export function TrimAll(code: string): string {
return code.trimStart()
}
}
// ------------------------------------------------------------------
// Const
// ------------------------------------------------------------------
/** Checks the value matches the next string */
// prettier-ignore
function NextTokenCheck(value: string, code: string): boolean {
if (value.length > code.length) return false
for (let i = 0; i < value.length; i++) {
if (value.charCodeAt(i) !== code.charCodeAt(i)) return false
}
return true
}
/** Gets the next constant string value or empty if no match */
// prettier-ignore
function NextConst(value: string, code: string, ): [] | [string, string] {
return NextTokenCheck(value, code)
? [code.slice(0, value.length), code.slice(value.length)]
: []
}
/** Takes the next constant string value skipping any whitespace */
// prettier-ignore
export function Const(value: string, code: string): [] | [string, string] {
if(value.length === 0) return ['', code]
const char_0 = value.charCodeAt(0)
return (
Chars.IsNewline(char_0) ? NextConst(value, Trim.TrimWhitespaceOnly(code)) :
Chars.IsWhitespace(char_0) ? NextConst(value, code) :
NextConst(value, Trim.TrimAll(code))
)
}
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
// prettier-ignore
function IdentIsFirst(char: number) {
return (
Chars.IsAlpha(char) ||
Chars.IsDollarSign(char) ||
Chars.IsUnderscore(char)
)
}
// prettier-ignore
function IdentIsRest(char: number) {
return (
Chars.IsAlpha(char) ||
Chars.IsDigit(char) ||
Chars.IsDollarSign(char) ||
Chars.IsUnderscore(char)
)
}
// prettier-ignore
function NextIdent(code: string): [] | [string, string] {
if (!IdentIsFirst(code.charCodeAt(0))) return []
for (let i = 1; i < code.length; i++) {
const char = code.charCodeAt(i)
if (IdentIsRest(char)) continue
const slice = code.slice(0, i)
const rest = code.slice(i)
return [slice, rest]
}
return [code, '']
}
/** Scans for the next Ident token */
// prettier-ignore
export function Ident(code: string): [] | [string, string] {
return NextIdent(Trim.TrimAll(code))
}
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
/** Checks that the next number is not a leading zero */
// prettier-ignore
function NumberLeadingZeroCheck(code: string, index: number) {
const char_0 = code.charCodeAt(index + 0)
const char_1 = code.charCodeAt(index + 1)
return (
(
// 1-9
Chars.IsNonZero(char_0)
) || (
// 0
Chars.IsZero(char_0) &&
!Chars.IsDigit(char_1)
) || (
// 0.
Chars.IsZero(char_0) &&
Chars.IsDot(char_1)
) || (
// .0
Chars.IsDot(char_0) &&
Chars.IsDigit(char_1)
)
)
}
/** Gets the next number token */
// prettier-ignore
function NextNumber(code: string): [] | [string, string] {
const negated = code.charAt(0) === '-'
const index = negated ? 1 : 0
if (!NumberLeadingZeroCheck(code, index)) {
return []
}
const dash = negated ? '-' : ''
let hasDot = false
for (let i = index; i < code.length; i++) {
const char_i = code.charCodeAt(i)
if (Chars.IsDigit(char_i)) {
continue
}
if (Chars.IsDot(char_i)) {
if (hasDot) {
const slice = code.slice(index, i)
const rest = code.slice(i)
return [`${dash}${slice}`, rest]
}
hasDot = true
continue
}
const slice = code.slice(index, i)
const rest = code.slice(i)
return [`${dash}${slice}`, rest]
}
return [code, '']
}
/** Scans for the next number token */
// prettier-ignore
export function Number(code: string) {
return NextNumber(Trim.TrimAll(code))
}
// ------------------------------------------------------------------
// String
// ------------------------------------------------------------------
// prettier-ignore
function NextString(options: string[], code: string): [] | [string, string] {
const first = code.charAt(0)
if(!options.includes(first)) return []
const quote = first
for(let i = 1; i < code.length; i++) {
const char = code.charAt(i)
if(char === quote) {
const slice = code.slice(1, i)
const rest = code.slice(i + 1)
return [slice, rest]
}
}
return []
}
/** Scans the next Literal String value */
// prettier-ignore
export function String(options: string[], code: string) {
return NextString(options, Trim.TrimAll(code))
}

250
src/parser/runtime/types.ts Normal file
View File

@@ -0,0 +1,250 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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.
---------------------------------------------------------------------------*/
export type IModuleProperties = Record<PropertyKey, IParser>
// ------------------------------------------------------------------
// Static
// ------------------------------------------------------------------
/** Force output static type evaluation for Arrays */
export type StaticEnsure<T> = T extends infer R ? R : never
/** Infers the Output Parameter for a Parser */
export type StaticParser<Parser extends IParser> = Parser extends IParser<infer Output extends unknown> ? Output : unknown
// ------------------------------------------------------------------
// Mapping
// ------------------------------------------------------------------
export type IMapping<Input extends unknown = any, Output extends unknown = unknown> = (input: Input, context: any) => Output
/** Maps input to output. This is the default Mapping */
export const Identity = (value: unknown) => value
/** Maps the output as the given parameter T */
// prettier-ignore
export const As = <T>(mapping: T): ((value: unknown) => T) => (_: unknown) => mapping
// ------------------------------------------------------------------
// Parser
// ------------------------------------------------------------------
export interface IParser<Output extends unknown = unknown> {
type: string
mapping: IMapping<any, Output>
}
// ------------------------------------------------------------------
// Context
// ------------------------------------------------------------------
// prettier-ignore
export type ContextParameter<_Left extends IParser, Right extends IParser> = (
StaticParser<Right>
)
export interface IContext<Output extends unknown = unknown> extends IParser<Output> {
type: 'Context'
left: IParser
right: IParser
}
/** `[Context]` Creates a Context Parser */
export function Context<Left extends IParser, Right extends IParser, Mapping extends IMapping = IMapping<ContextParameter<Left, Right>>>(left: Left, right: Right, mapping: Mapping): IContext<ReturnType<Mapping>>
/** `[Context]` Creates a Context Parser */
export function Context<Left extends IParser, Right extends IParser>(left: Left, right: Right): IContext<ContextParameter<Left, Right>>
/** `[Context]` Creates a Context Parser */
export function Context(...args: unknown[]): never {
const [left, right, mapping] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], args[1], Identity]
return { type: 'Context', left, right, mapping } as never
}
// ------------------------------------------------------------------
// Array
// ------------------------------------------------------------------
// prettier-ignore
export type ArrayParameter<Parser extends IParser> = StaticEnsure<
StaticParser<Parser>[]
>
export interface IArray<Output extends unknown = unknown> extends IParser<Output> {
type: 'Array'
parser: IParser
}
/** `[EBNF]` Creates an Array Parser */
export function Array<Parser extends IParser, Mapping extends IMapping = IMapping<ArrayParameter<Parser>>>(parser: Parser, mapping: Mapping): IArray<ReturnType<Mapping>>
/** `[EBNF]` Creates an Array Parser */
export function Array<Parser extends IParser>(parser: Parser): IArray<ArrayParameter<Parser>>
/** `[EBNF]` Creates an Array Parser */
export function Array(...args: unknown[]): never {
const [parser, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Array', parser, mapping } as never
}
// ------------------------------------------------------------------
// Const
// ------------------------------------------------------------------
export interface IConst<Output extends unknown = unknown> extends IParser<Output> {
type: 'Const'
value: string
}
/** `[TERM]` Creates a Const Parser */
export function Const<Value extends string, Mapping extends IMapping<Value>>(value: Value, mapping: Mapping): IConst<ReturnType<Mapping>>
/** `[TERM]` Creates a Const Parser */
export function Const<Value extends string>(value: Value): IConst<Value>
/** `[TERM]` Creates a Const Parser */
export function Const(...args: unknown[]): never {
const [value, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Const', value, mapping } as never
}
// ------------------------------------------------------------------
// Ref
// ------------------------------------------------------------------
export interface IRef<Output extends unknown = unknown> extends IParser<Output> {
type: 'Ref'
ref: string
}
/** `[BNF]` Creates a Ref Parser. This Parser can only be used in the context of a Module */
export function Ref<Type extends unknown, Mapping extends IMapping<Type>>(ref: string, mapping: Mapping): IRef<ReturnType<Mapping>>
/** `[BNF]` Creates a Ref Parser. This Parser can only be used in the context of a Module */
export function Ref<Type extends unknown>(ref: string): IRef<Type>
/** `[BNF]` Creates a Ref Parser. This Parser can only be used in the context of a Module */
export function Ref(...args: unknown[]): never {
const [ref, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Ref', ref, mapping } as never
}
// ------------------------------------------------------------------
// String
// ------------------------------------------------------------------
export interface IString<Output extends unknown = unknown> extends IParser<Output> {
type: 'String'
options: string[]
}
/** `[TERM]` Creates a String Parser. Options are an array of permissable quote characters */
export function String<Mapping extends IMapping<string>>(options: string[], mapping: Mapping): IString<ReturnType<Mapping>>
/** `[TERM]` Creates a String Parser. Options are an array of permissable quote characters */
export function String(options: string[]): IString<string>
/** `[TERM]` Creates a String Parser. Options are an array of permissable quote characters */
export function String(...params: unknown[]): never {
const [options, mapping] = params.length === 2 ? [params[0], params[1]] : [params[0], Identity]
return { type: 'String', options, mapping } as never
}
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
export interface IIdent<Output extends unknown = unknown> extends IParser<Output> {
type: 'Ident'
}
/** `[TERM]` Creates an Ident Parser where Ident matches any valid JavaScript identifier */
export function Ident<Mapping extends IMapping<string>>(mapping: Mapping): IIdent<ReturnType<Mapping>>
/** `[TERM]` Creates an Ident Parser where Ident matches any valid JavaScript identifier */
export function Ident(): IIdent<string>
/** `[TERM]` Creates an Ident Parser where Ident matches any valid JavaScript identifier */
export function Ident(...params: unknown[]): never {
const mapping = params.length === 1 ? params[0] : Identity
return { type: 'Ident', mapping } as never
}
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
export interface INumber<Output extends unknown = unknown> extends IParser<Output> {
type: 'Number'
}
/** `[TERM]` Creates an Number Parser */
export function Number<Mapping extends IMapping<string>>(mapping: Mapping): INumber<ReturnType<Mapping>>
/** `[TERM]` Creates an Number Parser */
export function Number(): INumber<string>
/** `[TERM]` Creates an Number Parser */
export function Number(...params: unknown[]): never {
const mapping = params.length === 1 ? params[0] : Identity
return { type: 'Number', mapping } as never
}
// ------------------------------------------------------------------
// Optional
// ------------------------------------------------------------------
// prettier-ignore
export type OptionalParameter<Parser extends IParser, Result extends unknown = [StaticParser<Parser>] | []> = (
Result
)
export interface IOptional<Output extends unknown = unknown> extends IParser<Output> {
type: 'Optional'
parser: IParser
}
/** `[EBNF]` Creates an Optional Parser */
export function Optional<Parser extends IParser, Mapping extends IMapping = IMapping<OptionalParameter<Parser>>>(parser: Parser, mapping: Mapping): IOptional<ReturnType<Mapping>>
/** `[EBNF]` Creates an Optional Parser */
export function Optional<Parser extends IParser>(parser: Parser): IOptional<OptionalParameter<Parser>>
/** `[EBNF]` Creates an Optional Parser */
export function Optional(...args: unknown[]): never {
const [parser, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Optional', parser, mapping } as never
}
// ------------------------------------------------------------------
// Tuple
// ------------------------------------------------------------------
// prettier-ignore
export type TupleParameter<Parsers extends IParser[], Result extends unknown[] = []> = StaticEnsure<
Parsers extends [infer Left extends IParser, ...infer Right extends IParser[]]
? TupleParameter<Right, [...Result, StaticEnsure<StaticParser<Left>>]>
: Result
>
export interface ITuple<Output extends unknown = unknown> extends IParser<Output> {
type: 'Tuple'
parsers: IParser[]
}
/** `[BNF]` Creates a Tuple Parser */
export function Tuple<Parsers extends IParser[], Mapping extends IMapping = IMapping<TupleParameter<Parsers>>>(parsers: [...Parsers], mapping: Mapping): ITuple<ReturnType<Mapping>>
/** `[BNF]` Creates a Tuple Parser */
export function Tuple<Parsers extends IParser[]>(parsers: [...Parsers]): ITuple<TupleParameter<Parsers>>
/** `[BNF]` Creates a Tuple Parser */
export function Tuple(...args: unknown[]): never {
const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Tuple', parsers, mapping } as never
}
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
// prettier-ignore
export type UnionParameter<Parsers extends IParser[], Result extends unknown = never> = StaticEnsure<
Parsers extends [infer Left extends IParser, ...infer Right extends IParser[]]
? UnionParameter<Right, Result | StaticParser<Left>>
: Result
>
export interface IUnion<Output extends unknown = unknown> extends IParser<Output> {
type: 'Union'
parsers: IParser[]
}
/** `[BNF]` Creates a Union parser */
export function Union<Parsers extends IParser[], Mapping extends IMapping = IMapping<UnionParameter<Parsers>>>(parsers: [...Parsers], mapping: Mapping): IUnion<ReturnType<Mapping>>
/** `[BNF]` Creates a Union parser */
export function Union<Parsers extends IParser[]>(parsers: [...Parsers]): IUnion<UnionParameter<Parsers>>
/** `[BNF]` Creates a Union parser */
export function Union(...args: unknown[]): never {
const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Union', parsers, mapping } as never
}

View File

@@ -0,0 +1,31 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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.
---------------------------------------------------------------------------*/
export * as Token from './token'
export * from './parse'
export * from './types'

153
src/parser/static/parse.ts Normal file
View File

@@ -0,0 +1,153 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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 Tokens from './token'
import * as Types from './types'
// ------------------------------------------------------------------
// Context
// ------------------------------------------------------------------
// prettier-ignore
type ContextParser<Left extends Types.IParser, Right extends Types.IParser, Code extends string, Context extends unknown> = (
Parse<Left, Code, Context> extends [infer Context extends unknown, infer Rest extends string]
? Parse<Right, Rest, Context>
: []
)
// ------------------------------------------------------------------
// Array
// ------------------------------------------------------------------
// prettier-ignore
type ArrayParser<Parser extends Types.IParser, Code extends string, Context extends unknown, Result extends unknown[] = []> = (
Parse<Parser, Code, Context> extends [infer Value1 extends unknown, infer Rest extends string]
? ArrayParser<Parser, Rest, Context, [...Result, Value1]>
: [Result, Code]
)
// ------------------------------------------------------------------
// Const
// ------------------------------------------------------------------
// prettier-ignore
type ConstParser<Value extends string, Code extends string, _Context extends unknown> = (
Tokens.Const<Value, Code> extends [infer Match extends Value, infer Rest extends string]
? [Match, Rest]
: []
)
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
// prettier-ignore
type IdentParser<Code extends string, _Context extends unknown> = (
Tokens.Ident<Code> extends [infer Match extends string, infer Rest extends string]
? [Match, Rest]
: []
)
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
// prettier-ignore
type NumberParser<Code extends string, _Context extends unknown> = (
Tokens.Number<Code> extends [infer Match extends string, infer Rest extends string]
? [Match, Rest]
: []
)
// ------------------------------------------------------------------
// Optional
// ------------------------------------------------------------------
// prettier-ignore
type OptionalParser<Parser extends Types.IParser, Code extends string, Context extends unknown> = (
Parse<Parser, Code, Context> extends [infer Value extends unknown, infer Rest extends string]
? [[Value], Rest]
: [[], Code]
)
// ------------------------------------------------------------------
// String
// ------------------------------------------------------------------
// prettier-ignore
type StringParser<Options extends string[], Code extends string, _Context extends unknown> = (
Tokens.String<Options, Code> extends [infer Match extends string, infer Rest extends string]
? [Match, Rest]
: []
)
// ------------------------------------------------------------------
// Tuple
// ------------------------------------------------------------------
// prettier-ignore
type TupleParser<Parsers extends Types.IParser[], Code extends string, Context extends unknown, Result extends unknown[] = []> = (
Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]]
? Parse<Left, Code, Context> extends [infer Value extends unknown, infer Rest extends string]
? TupleParser<Right, Rest, Context, [...Result, Value]>
: []
: [Result, Code]
)
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
// prettier-ignore
type UnionParser<Parsers extends Types.IParser[], Code extends string, Context extends unknown> = (
Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]]
? Parse<Left, Code, Context> extends [infer Value extends unknown, infer Rest extends string]
? [Value, Rest]
: UnionParser<Right, Code, Context>
: []
)
// ------------------------------------------------------------------
// Parse
// ------------------------------------------------------------------
// prettier-ignore
type ParseCode<Type extends Types.IParser, Code extends string, Context extends unknown = unknown> = (
Type extends Types.Context<infer Left extends Types.IParser, infer Right extends Types.IParser> ? ContextParser<Left, Right, Code, Context> :
Type extends Types.Array<infer Parser extends Types.IParser> ? ArrayParser<Parser, Code, Context> :
Type extends Types.Const<infer Value extends string> ? ConstParser<Value, Code, Context> :
Type extends Types.Ident ? IdentParser<Code, Context> :
Type extends Types.Number ? NumberParser<Code, Context> :
Type extends Types.Optional<infer Parser extends Types.IParser> ? OptionalParser<Parser, Code, Context> :
Type extends Types.String<infer Options extends string[]> ? StringParser<Options, Code, Context> :
Type extends Types.Tuple<infer Parsers extends Types.IParser[]> ? TupleParser<Parsers, Code, Context> :
Type extends Types.Union<infer Parsers extends Types.IParser[]> ? UnionParser<Parsers, Code, Context> :
[]
)
// prettier-ignore
type ParseMapping<Parser extends Types.IParser, Result extends unknown, Context extends unknown = unknown> = (
(Parser['mapping'] & { input: Result, context: Context })['output']
)
/** Parses code with the given parser */
// prettier-ignore
export type Parse<Type extends Types.IParser, Code extends string, Context extends unknown = unknown> = (
ParseCode<Type, Code, Context> extends [infer L extends unknown, infer R extends string]
? [ParseMapping<Type, L, Context>, R]
: []
)

213
src/parser/static/token.ts Normal file
View File

@@ -0,0 +1,213 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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.
---------------------------------------------------------------------------*/
// ------------------------------------------------------------------
// Chars
// ------------------------------------------------------------------
// prettier-ignore
namespace Chars {
export type Empty = ''
export type Space = ' '
export type Newline = '\n'
export type Dot = '.'
export type Hyphen = '-'
export type Digit = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
]
export type Alpha = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z'
]
}
// ------------------------------------------------------------------
// Trim
// ------------------------------------------------------------------
// prettier-ignore
namespace Trim {
// ------------------------------------------------------------------
// Whitespace Filters
// ------------------------------------------------------------------
type W9 = `${W8}${W8}` // 512
type W8 = `${W7}${W7}` // 256
type W7 = `${W6}${W6}` // 128
type W6 = `${W5}${W5}` // 64
type W5 = `${W4}${W4}` // 32
type W4 = `${W3}${W3}` // 16
type W3 = `${W2}${W2}` // 8
type W2 = `${W1}${W1}` // 4
type W1 = `${W0}${W0}` // 2
type W0 = ` ` // 1
// ------------------------------------------------------------------
// TrimWhitespace
// ------------------------------------------------------------------
/** Trims whitespace only */
export type TrimWhitespace<Code extends string> = (
Code extends `${W4}${infer Rest extends string}` ? TrimWhitespace<Rest> :
Code extends `${W3}${infer Rest extends string}` ? TrimWhitespace<Rest> :
Code extends `${W1}${infer Rest extends string}` ? TrimWhitespace<Rest> :
Code extends `${W0}${infer Rest extends string}` ? TrimWhitespace<Rest> :
Code
)
// ------------------------------------------------------------------
// Trim
// ------------------------------------------------------------------
/** Trims Whitespace and Newline */
export type TrimAll<Code extends string> = (
Code extends `${W4}${infer Rest extends string}` ? TrimAll<Rest> :
Code extends `${W3}${infer Rest extends string}` ? TrimAll<Rest> :
Code extends `${W1}${infer Rest extends string}` ? TrimAll<Rest> :
Code extends `${W0}${infer Rest extends string}` ? TrimAll<Rest> :
Code extends `${Chars.Newline}${infer Rest extends string}` ? TrimAll<Rest> :
Code
)
}
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
/** Scans for the next match union */
// prettier-ignore
type NextUnion<Variants extends string[], Code extends string> = (
Variants extends [infer Variant extends string, ...infer Rest1 extends string[]]
? NextConst<Variant, Code> extends [infer Match extends string, infer Rest2 extends string]
? [Match, Rest2]
: NextUnion<Rest1, Code>
: []
)
// ------------------------------------------------------------------
// Const
// ------------------------------------------------------------------
// prettier-ignore
type NextConst<Value extends string, Code extends string> = (
Code extends `${Value}${infer Rest extends string}`
? [Value, Rest]
: []
)
/** Scans for the next constant value */
// prettier-ignore
export type Const<Value extends string, Code extends string> = (
Value extends '' ? ['', Code] :
Value extends `${infer First extends string}${string}`
? (
First extends Chars.Newline ? NextConst<Value, Trim.TrimWhitespace<Code>> :
First extends Chars.Space ? NextConst<Value, Code> :
NextConst<Value, Trim.TrimAll<Code>>
) : never
)
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
// prettier-ignore
type NextNumberNegate<Code extends string> = (
Code extends `${Chars.Hyphen}${infer Rest extends string}`
? [Chars.Hyphen, Rest]
: [Chars.Empty, Code]
)
// prettier-ignore
type NextNumberZeroCheck<Code extends string> = (
Code extends `0${infer Rest}`
? NextUnion<Chars.Digit, Rest> extends [string, string] ? false : true
: true
)
// prettier-ignore
type NextNumberScan<Code extends string, HasDecimal extends boolean = false, Result extends string = Chars.Empty> = (
NextUnion<[...Chars.Digit, Chars.Dot], Code> extends [infer Char extends string, infer Rest extends string]
? Char extends Chars.Dot
? HasDecimal extends false
? NextNumberScan<Rest, true, `${Result}${Char}`>
: [Result, `.${Rest}`]
: NextNumberScan<Rest, HasDecimal, `${Result}${Char}`>
: [Result, Code]
)
// prettier-ignore
export type NextNumber<Code extends string> = (
NextNumberNegate<Code> extends [infer Negate extends string, infer Rest extends string]
? NextNumberZeroCheck<Rest> extends true
? NextNumberScan<Rest> extends [infer Number extends string, infer Rest2 extends string]
? Number extends Chars.Empty
? []
: [`${Negate}${Number}`, Rest2]
: []
: []
: []
)
/** Scans for the next literal number */
export type Number<Code extends string> = NextNumber<Trim.TrimAll<Code>>
// ------------------------------------------------------------------
// String
// ------------------------------------------------------------------
type NextStringQuote<Options extends string[], Code extends string> = NextUnion<Options, Code>
// prettier-ignore
type NextStringBody<Code extends string, Quote extends string, Result extends string = Chars.Empty> = (
Code extends `${infer Char extends string}${infer Rest extends string}`
? Char extends Quote
? [Result, Rest]
: NextStringBody<Rest, Quote, `${Result}${Char}`>
: []
)
// prettier-ignore
type NextString<Options extends string[], Code extends string> = (
NextStringQuote<Options, Code> extends [infer Quote extends string, infer Rest extends string]
? NextStringBody<Rest, Quote> extends [infer String extends string, infer Rest extends string]
? [String, Rest]
: []
: []
)
/** Scans for the next literal string */
export type String<Options extends string[], Code extends string> = NextString<Options, Trim.TrimAll<Code>>
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
type IdentLeft = [...Chars.Alpha, '_', '$'] // permissable first characters
type IdentRight = [...Chars.Digit, ...IdentLeft] // permissible subsequent characters
// prettier-ignore
type NextIdentScan<Code extends string, Result extends string = Chars.Empty> = (
NextUnion<IdentRight, Code> extends [infer Char extends string, infer Rest extends string]
? NextIdentScan<Rest, `${Result}${Char}`>
: [Result, Code]
)
// prettier-ignore
type NextIdent<Code extends string> = (
NextUnion<IdentLeft, Code> extends [infer Left extends string, infer Rest1 extends string]
? NextIdentScan<Rest1> extends [infer Right extends string, infer Rest2 extends string]
? [`${Left}${Right}`, Rest2]
: []
: []
)
/** Scans for the next Ident */
export type Ident<Code extends string> = NextIdent<Trim.TrimAll<Code>>

143
src/parser/static/types.ts Normal file
View File

@@ -0,0 +1,143 @@
/*--------------------------------------------------------------------------
@sinclair/parsebox
The MIT License (MIT)
Copyright (c) 2024 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.
---------------------------------------------------------------------------*/
// ------------------------------------------------------------------
// Mapping
// ------------------------------------------------------------------
/**
* `[ACTION]` Inference mapping base type. Used to specify semantic actions for
* Parser productions. This type is implemented as a higher-kinded type where
* productions are received on the `input` property with mapping assigned
* the `output` property. The parsing context is available on the `context`
* property.
*/
export interface IMapping {
context: unknown
input: unknown
output: unknown
}
/** `[ACTION]` Default inference mapping. */
export interface Identity extends IMapping {
output: this['input']
}
/** `[ACTION]` Maps the given argument `T` as the mapping output */
export interface As<T> extends IMapping {
output: T
}
// ------------------------------------------------------------------
// Parser
// ------------------------------------------------------------------
/** Base type Parser implemented by all other parsers */
export interface IParser<Mapping extends IMapping = Identity> {
type: string
mapping: Mapping
}
// ------------------------------------------------------------------
// Context
// ------------------------------------------------------------------
/** `[Context]` Creates a Context Parser */
export interface Context<Left extends IParser = IParser, Right extends IParser = IParser, Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Context'
left: Left
right: Right
}
// ------------------------------------------------------------------
// Array
// ------------------------------------------------------------------
/** `[EBNF]` Creates an Array Parser */
export interface Array<Parser extends IParser = IParser, Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Array'
parser: Parser
}
// ------------------------------------------------------------------
// Const
// ------------------------------------------------------------------
/** `[TERM]` Creates a Const Parser */
export interface Const<Value extends string = string, Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Const'
value: Value
}
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
/** `[TERM]` Creates an Ident Parser. */
// prettier-ignore
export interface Ident<Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Ident'
}
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
/** `[TERM]` Creates a Number Parser. */
// prettier-ignore
export interface Number<Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Number'
}
// ------------------------------------------------------------------
// Optional
// ------------------------------------------------------------------
/** `[EBNF]` Creates a Optional Parser */
export interface Optional<Parser extends IParser = IParser, Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Optional'
parser: Parser
}
// ------------------------------------------------------------------
// String
// ------------------------------------------------------------------
/** `[TERM]` Creates a String Parser. Options are an array of permissable quote characters */
export interface String<Options extends string[], Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'String'
quote: Options
}
// ------------------------------------------------------------------
// Tuple
// ------------------------------------------------------------------
/** `[BNF]` Creates a Tuple Parser */
export interface Tuple<Parsers extends IParser[] = [], Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Tuple'
parsers: [...Parsers]
}
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
/** `[BNF]` Creates a Union Parser */
export interface Union<Parsers extends IParser[] = [], Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Union'
parsers: [...Parsers]
}

29
src/syntax/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/syntax
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './syntax'

1030
src/syntax/mapping.ts Normal file

File diff suppressed because it is too large Load Diff

1686
src/syntax/parser.ts Normal file

File diff suppressed because it is too large Load Diff

62
src/syntax/syntax.ts Normal file
View File

@@ -0,0 +1,62 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/syntax
The MIT License (MIT)
Copyright (c) 2017-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 '../type/index'
import { Type, TType } from './parser'
// ------------------------------------------------------------------
// NoInfer
// ------------------------------------------------------------------
/** `[Experimental]` Parses type expressions into TypeBox types but does not infer */
export function NoInfer<Context extends Record<PropertyKey, t.TSchema>, Input extends string>(context: Context, input: Input, options?: t.SchemaOptions): t.TSchema
/** `[Experimental]` Parses type expressions into TypeBox types but does not infer */
export function NoInfer<Input extends string>(input: Input, options?: t.SchemaOptions): t.TSchema
/** `[Experimental]` Parses type expressions into TypeBox types but does not infer */
// prettier-ignore
export function NoInfer(...args: any[]): t.TSchema {
const withContext = typeof args[0] === 'string' ? false : true
const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}]
const result = Type(code, context)[0]
return t.KindGuard.IsSchema(result)
? t.CloneType(result, options)
: t.Never(options)
}
/** `[Experimental]` Parses type expressions into TypeBox types */
// prettier-ignore
export type TSyntax<Context extends Record<PropertyKey, t.TSchema>, Code extends string> = (
TType<Code, Context> extends [infer Type extends t.TSchema, string] ? Type : t.TNever
)
/** `[Experimental]` Parses type expressions into TypeBox types */
export function Syntax<Context extends Record<PropertyKey, t.TSchema>, Input extends string>(context: Context, input: Input, options?: t.SchemaOptions): TSyntax<Context, Input>
/** `[Experimental]` Parses type expressions into TypeBox types */
export function Syntax<Input extends string>(annotation: Input, options?: t.SchemaOptions): TSyntax<{}, Input>
/** `[Experimental]` Parses type expressions into TypeBox types */
export function Syntax(...args: any[]): never {
return NoInfer.apply(null, args as never) as never
}

30
src/system/index.ts Normal file
View File

@@ -0,0 +1,30 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/system
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './policy'
export * from './system'

77
src/system/policy.ts Normal file
View File

@@ -0,0 +1,77 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/system
The MIT License (MIT)
Copyright (c) 2017-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 { IsObject, IsArray, IsNumber, IsUndefined } from '../value/guard/index'
export namespace TypeSystemPolicy {
// ------------------------------------------------------------------
// TypeSystemPolicy: Instancing
// ------------------------------------------------------------------
/**
* Configures the instantiation behavior of TypeBox types. The `default` option assigns raw JavaScript
* references for embedded types, which may cause side effects if type properties are explicitly updated
* outside the TypeBox type builder. The `clone` option creates copies of any shared types upon creation,
* preventing unintended side effects. The `freeze` option applies `Object.freeze()` to the type, making
* it fully readonly and immutable. Implementations should use `default` whenever possible, as it is the
* fastest way to instantiate types. The default setting is `default`.
*/
export let InstanceMode: 'default' | 'clone' | 'freeze' = 'default'
// ------------------------------------------------------------------
// TypeSystemPolicy: Checking
// ------------------------------------------------------------------
/** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */
export let ExactOptionalPropertyTypes: boolean = false
/** Sets whether arrays should be treated as a kind of objects. The default is `false` */
export let AllowArrayObject: boolean = false
/** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */
export let AllowNaN: boolean = false
/** Sets whether `null` should validate for void types. The default is `false` */
export let AllowNullVoid: boolean = false
/** Checks this value using the ExactOptionalPropertyTypes policy */
export function IsExactOptionalProperty(value: Record<keyof any, unknown>, key: string) {
return ExactOptionalPropertyTypes ? key in value : value[key] !== undefined
}
/** Checks this value using the AllowArrayObjects policy */
export function IsObjectLike(value: unknown): value is Record<keyof any, unknown> {
const isObject = IsObject(value)
return AllowArrayObject ? isObject : isObject && !IsArray(value)
}
/** Checks this value as a record using the AllowArrayObjects policy */
export function IsRecordLike(value: unknown): value is Record<keyof any, unknown> {
return IsObjectLike(value) && !(value instanceof Date) && !(value instanceof Uint8Array)
}
/** Checks this value using the AllowNaN policy */
export function IsNumberLike(value: unknown): value is number {
return AllowNaN ? IsNumber(value) : Number.isFinite(value)
}
/** Checks this value using the AllowVoidNull policy */
export function IsVoidLike(value: unknown): value is void {
const isUndefined = IsUndefined(value)
return AllowNullVoid ? isUndefined || value === null : isUndefined
}
}

66
src/system/system.ts Normal file
View File

@@ -0,0 +1,66 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/system
The MIT License (MIT)
Copyright (c) 2017-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 { TypeRegistry, FormatRegistry } from '../type/registry/index'
import { Unsafe, type TUnsafe } from '../type/unsafe/index'
import { Kind } from '../type/symbols/index'
import { TypeBoxError } from '../type/error/index'
// ------------------------------------------------------------------
// Errors
// ------------------------------------------------------------------
export class TypeSystemDuplicateTypeKind extends TypeBoxError {
constructor(kind: string) {
super(`Duplicate type kind '${kind}' detected`)
}
}
export class TypeSystemDuplicateFormat extends TypeBoxError {
constructor(kind: string) {
super(`Duplicate string format '${kind}' detected`)
}
}
// ------------------------------------------------------------------
// TypeSystem
// ------------------------------------------------------------------
export type TypeFactoryFunction<Type, Options = Record<PropertyKey, unknown>> = (options?: Partial<Options>) => TUnsafe<Type>
/** Creates user defined types and formats and provides overrides for value checking behaviours */
export namespace TypeSystem {
/** Creates a new type */
export function Type<Type, Options = Record<PropertyKey, unknown>>(kind: string, check: (options: Options, value: unknown) => boolean): TypeFactoryFunction<Type, Options> {
if (TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind)
TypeRegistry.Set(kind, check)
return (options: Partial<Options> = {}) => Unsafe<Type>({ ...options, [Kind]: kind })
}
/** Creates a new string format */
export function Format<F extends string>(format: F, check: (value: string) => boolean): F {
if (FormatRegistry.Has(format)) throw new TypeSystemDuplicateFormat(format)
FormatRegistry.Set(format, check)
return format
}
}

4
src/tsconfig.json Normal file
View File

@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"files": ["compiler/index.ts", "errors/index.ts", "parser/index.ts", "syntax/index.ts", "system/index.ts", "type/index.ts", "value/index.ts", "index.ts"]
}

41
src/type/any/any.ts Normal file
View File

@@ -0,0 +1,41 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/index'
import type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'
export interface TAny extends TSchema {
[Kind]: 'Any'
static: any
}
/** `[Json]` Creates an Any type */
export function Any(options?: SchemaOptions): TAny {
return CreateType({ [Kind]: 'Any' }, options) as never
}

29
src/type/any/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './any'

View File

@@ -0,0 +1,41 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import type { TSchema } from '../schema/index'
import { Kind } from '../symbols/index'
export interface TArgument<Index extends number = number> extends TSchema {
[Kind]: 'Argument'
static: unknown
index: Index
}
/** `[JavaScript]` Creates an Argument Type. */
export function Argument<Index extends number>(index: Index): TArgument<Index> {
return CreateType({ [Kind]: 'Argument', index }) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './argument'

59
src/type/array/array.ts Normal file
View File

@@ -0,0 +1,59 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import { Ensure } from '../helpers/index'
import type { SchemaOptions, TSchema } from '../schema/index'
import type { Static } from '../static/index'
import { Kind } from '../symbols/index'
export interface ArrayOptions extends SchemaOptions {
/** The minimum number of items in this array */
minItems?: number
/** The maximum number of items in this array */
maxItems?: number
/** Should this schema contain unique items */
uniqueItems?: boolean
/** A schema for which some elements should match */
contains?: TSchema
/** A minimum number of contains schema matches */
minContains?: number
/** A maximum number of contains schema matches */
maxContains?: number
}
type ArrayStatic<T extends TSchema, P extends unknown[]> = Ensure<Static<T, P>[]>
export interface TArray<T extends TSchema = TSchema> extends TSchema, ArrayOptions {
[Kind]: 'Array'
static: ArrayStatic<T, this['params']>
type: 'array'
items: T
}
/** `[Json]` Creates an Array type */
export function Array<Type extends TSchema>(items: Type, options?: ArrayOptions): TArray<Type> {
return CreateType({ [Kind]: 'Array', type: 'array', items }, options) as never
}

29
src/type/array/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './array'

View File

@@ -0,0 +1,43 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import type { Static } from '../static/index'
import { Kind } from '../symbols/index'
import { CreateType } from '../create/type'
export interface TAsyncIterator<T extends TSchema = TSchema> extends TSchema {
[Kind]: 'AsyncIterator'
static: AsyncIterableIterator<Static<T, this['params']>>
type: 'AsyncIterator'
items: T
}
/** `[JavaScript]` Creates a AsyncIterator type */
export function AsyncIterator<T extends TSchema>(items: T, options?: SchemaOptions): TAsyncIterator<T> {
return CreateType({ [Kind]: 'AsyncIterator', type: 'AsyncIterator', items }, options) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './async-iterator'

121
src/type/awaited/awaited.ts Normal file
View File

@@ -0,0 +1,121 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import { Ensure } from '../helpers/index'
import type { TSchema, SchemaOptions } from '../schema/index'
import { Computed, type TComputed } from '../computed/index'
import { Intersect, type TIntersect } from '../intersect/index'
import { Union, type TUnion } from '../union/index'
import { type TPromise } from '../promise/index'
import { Ref, type TRef } from '../ref/index'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsIntersect, IsUnion, IsPromise, IsRef, IsComputed } from '../guard/kind'
// ----------------------------------------------------------------
// FromComputed
// ----------------------------------------------------------------
// prettier-ignore
type TFromComputed<Target extends string, Parameters extends TSchema[]> = Ensure<(
TComputed<'Awaited', [TComputed<Target, Parameters>]>
)>
// prettier-ignore
function FromComputed<Target extends string, Parameters extends TSchema[]>(target: Target, parameters: Parameters): TFromComputed<Target, Parameters> {
return Computed('Awaited', [Computed(target, parameters)]) as never
}
// ----------------------------------------------------------------
// Ref
// ----------------------------------------------------------------
type TFromRef<Ref extends string> = Ensure<TComputed<'Awaited', [TRef<Ref>]>>
// prettier-ignore
function FromRef<Ref extends string>($ref: Ref): TFromRef<Ref> {
return Computed('Awaited', [Ref($ref)]) as never
}
// ----------------------------------------------------------------
// FromIntersect
// ----------------------------------------------------------------
// prettier-ignore
type TFromIntersect<Types extends TSchema[]> = (
TIntersect<TFromRest<Types>>
)
// prettier-ignore
function FromIntersect<Types extends TSchema[]>(types: [...Types]): TFromIntersect<Types> {
return Intersect(FromRest(types) as TSchema[]) as never
}
// ----------------------------------------------------------------
// FromUnion
// ----------------------------------------------------------------
// prettier-ignore
type TFromUnion<Types extends TSchema[]> = TUnion<TFromRest<Types>>
// prettier-ignore
function FromUnion<Types extends TSchema[]>(types: [...Types]): TFromUnion<Types> {
return Union(FromRest(types) as TSchema[]) as never
}
// ----------------------------------------------------------------
// Promise
// ----------------------------------------------------------------
type TFromPromise<Type extends TSchema> = TAwaited<Type>
// prettier-ignore
function FromPromise<Type extends TSchema>(type: Type): TFromPromise<Type> {
return Awaited(type) as never
}
// ------------------------------------------------------------------
// FromRest
// ------------------------------------------------------------------
// prettier-ignore
type TFromRest<Types extends TSchema[], Result extends TSchema[] = []> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? TFromRest<Right, [...Result, TAwaited<Left>]>
: Result
)
// prettier-ignore
function FromRest<Types extends TSchema[]>(types: [...Types]) : TFromRest<Types> {
return types.map(type => Awaited(type)) as never
}
// ------------------------------------------------------------------
// TAwaited
// ------------------------------------------------------------------
// prettier-ignore
export type TAwaited<Type extends TSchema> = (
Type extends TComputed<infer Target extends string, infer Parameters extends TSchema[]> ? TFromComputed<Target, Parameters> :
Type extends TRef<infer Ref extends string> ? TFromRef<Ref> :
Type extends TIntersect<infer Types extends TSchema[]> ? TIntersect<TFromRest<Types>> :
Type extends TUnion<infer Types extends TSchema[]> ? TUnion<TFromRest<Types>> :
Type extends TPromise<infer Type extends TSchema> ? TAwaited<Type> :
Type
)
/** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */
export function Awaited<T extends TSchema>(type: T, options?: SchemaOptions): TAwaited<T> {
return CreateType(
IsComputed(type) ? FromComputed(type.target, type.parameters) : IsIntersect(type) ? FromIntersect(type.allOf) : IsUnion(type) ? FromUnion(type.anyOf) : IsPromise(type) ? FromPromise(type.item) : IsRef(type) ? FromRef(type.$ref) : type,
options,
) as never
}

29
src/type/awaited/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './awaited'

48
src/type/bigint/bigint.ts Normal file
View File

@@ -0,0 +1,48 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'
import { CreateType } from '../create/index'
export interface BigIntOptions extends SchemaOptions {
exclusiveMaximum?: bigint
exclusiveMinimum?: bigint
maximum?: bigint
minimum?: bigint
multipleOf?: bigint
}
export interface TBigInt extends TSchema, BigIntOptions {
[Kind]: 'BigInt'
static: bigint
type: 'bigint'
}
/** `[JavaScript]` Creates a BigInt type */
export function BigInt(options?: BigIntOptions): TBigInt {
return CreateType({ [Kind]: 'BigInt', type: 'bigint' }, options) as never
}

29
src/type/bigint/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './bigint'

View File

@@ -0,0 +1,41 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'
import { CreateType } from '../create/index'
export interface TBoolean extends TSchema {
[Kind]: 'Boolean'
static: boolean
type: 'boolean'
}
/** `[Json]` Creates a Boolean type */
export function Boolean(options?: SchemaOptions): TBoolean {
return CreateType({ [Kind]: 'Boolean', type: 'boolean' }, options) as never
}

29
src/type/boolean/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './boolean'

30
src/type/clone/index.ts Normal file
View File

@@ -0,0 +1,30 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './type'
export * from './value'

39
src/type/clone/type.ts Normal file
View File

@@ -0,0 +1,39 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { TSchema, SchemaOptions } from '../schema/index'
import { Clone } from './value'
/** Clones a Rest */
export function CloneRest<T extends TSchema[]>(schemas: T): T {
return schemas.map((schema) => CloneType(schema)) as never
}
/** Clones a Type */
export function CloneType<T extends TSchema>(schema: T, options?: SchemaOptions): T {
return options === undefined ? Clone(schema) : Clone({ ...options, ...schema })
}

67
src/type/clone/value.ts Normal file
View File

@@ -0,0 +1,67 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 ValueGuard from '../guard/value'
function ArrayType(value: unknown[]) {
return (value as any).map((value: unknown) => Visit(value as any))
}
function DateType(value: Date) {
return new Date(value.getTime())
}
function Uint8ArrayType(value: Uint8Array) {
return new Uint8Array(value)
}
function RegExpType(value: RegExp) {
return new RegExp(value.source, value.flags)
}
function ObjectType(value: Record<keyof any, unknown>) {
const result = {} as Record<PropertyKey, unknown>
for (const key of Object.getOwnPropertyNames(value)) {
result[key] = Visit(value[key])
}
for (const key of Object.getOwnPropertySymbols(value)) {
result[key] = Visit(value[key])
}
return result
}
// prettier-ignore
function Visit(value: unknown): any {
return (
ValueGuard.IsArray(value) ? ArrayType(value) :
ValueGuard.IsDate(value) ? DateType(value) :
ValueGuard.IsUint8Array(value) ? Uint8ArrayType(value) :
ValueGuard.IsRegExp(value) ? RegExpType(value) :
ValueGuard.IsObject(value) ? ObjectType(value) :
value
)
}
/** Clones a value */
export function Clone<T>(value: T): T {
return Visit(value)
}

View File

@@ -0,0 +1,122 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema } from '../schema/index'
import type { Evaluate } from '../helpers/index'
import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index'
import { IndexFromPropertyKeys, type TIndexFromPropertyKeys } from '../indexed/index'
import { KeyOfPropertyKeys, type TKeyOfPropertyKeys } from '../keyof/index'
import { type TNever } from '../never/index'
import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index'
import { SetDistinct, TSetDistinct } from '../sets/index'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsNever } from '../guard/kind'
// ------------------------------------------------------------------
// CompositeKeys
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeKeys<T extends TSchema[], Acc extends PropertyKey[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TCompositeKeys<R, [...Acc, ...TKeyOfPropertyKeys<L>]>
: TSetDistinct<Acc>
)
// prettier-ignore
function CompositeKeys<T extends TSchema[]>(T: [...T]): TCompositeKeys<T> {
const Acc = [] as PropertyKey[]
for(const L of T) Acc.push(...KeyOfPropertyKeys(L))
return SetDistinct(Acc) as never
}
// ------------------------------------------------------------------
// FilterNever
// ------------------------------------------------------------------
// prettier-ignore
type TFilterNever<T extends TSchema[], Acc extends TSchema[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? L extends TNever
? TFilterNever<R, [...Acc]>
: TFilterNever<R, [...Acc, L]>
: Acc
)
// prettier-ignore
function FilterNever<T extends TSchema[]>(T: [...T]): TFilterNever<T> {
return T.filter(L => !IsNever(L)) as never
}
// ------------------------------------------------------------------
// CompositeProperty
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeProperty<T extends TSchema[], K extends PropertyKey, Acc extends TSchema[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TCompositeProperty<R, K, [...Acc, ...TIndexFromPropertyKeys<L, [K]>]>
: TFilterNever<Acc>
)
// prettier-ignore
function CompositeProperty<T extends TSchema[], K extends PropertyKey>(T: [...T], K: K): TCompositeProperty<T, K> {
const Acc = [] as TSchema[]
for(const L of T) Acc.push(...IndexFromPropertyKeys(L, [K]))
return FilterNever(Acc) as never
}
// ------------------------------------------------------------------
// CompositeProperties
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeProperties<T extends TSchema[], K extends PropertyKey[], Acc = {}> = (
K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TCompositeProperties<T, R, Acc & { [_ in L]: TIntersectEvaluated<TCompositeProperty<T, L>> }>
: Acc
)
// prettier-ignore
function CompositeProperties<T extends TSchema[], K extends PropertyKey[] = []>(T: [...T], K: [...K]): TCompositeProperties<T, K> {
const Acc = {} as never
for(const L of K) {
Acc[L] = IntersectEvaluated(CompositeProperty(T, L))
}
return Acc
}
// ------------------------------------------------------------------
// Composite
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeEvaluate<
T extends TSchema[],
K extends PropertyKey[] = TCompositeKeys<T>,
P extends TProperties = Evaluate<TCompositeProperties<T, K>>,
R extends TSchema = TObject<P>
> = R
// prettier-ignore
export type TComposite<T extends TSchema[]> = TCompositeEvaluate<T>
// prettier-ignore
export function Composite<T extends TSchema[]>(T: [...T], options?: ObjectOptions): TComposite<T> {
const K = CompositeKeys(T)
const P = CompositeProperties(T, K)
const R = Object(P, options)
return R as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './composite'

View File

@@ -0,0 +1,44 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import { CreateType } from '../create/index'
import { Kind } from '../symbols/symbols'
// ------------------------------------------------------------------
// Computed
// ------------------------------------------------------------------
export interface TComputed<Target extends string = string, Parameters extends TSchema[] = []> extends TSchema {
[Kind]: 'Computed'
target: Target
parameters: Parameters
}
/** `[Internal]` Creates a deferred computed type. This type is used exclusively in modules to defer resolution of computable types that contain interior references */
export function Computed<Target extends string, Parameters extends TSchema[]>(target: Target, parameters: [...Parameters], options?: SchemaOptions): TComputed<Target, Parameters> {
return CreateType({ [Kind]: 'Computed', target, parameters }, options) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './computed'

135
src/type/const/const.ts Normal file
View File

@@ -0,0 +1,135 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { AssertRest, Evaluate } from '../helpers/index'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { TProperties } from '../object/index'
import { Any, type TAny } from '../any/index'
import { BigInt, type TBigInt } from '../bigint/index'
import { Date, type TDate } from '../date/index'
import { Function as FunctionType, type TFunction } from '../function/index'
import { Literal, type TLiteral } from '../literal/index'
import { type TNever } from '../never/index'
import { Null, type TNull } from '../null/index'
import { Object, type TObject } from '../object/index'
import { Symbol, type TSymbol } from '../symbol/index'
import { Tuple, type TTuple } from '../tuple/index'
import { Readonly, type TReadonly } from '../readonly/index'
import { Undefined, type TUndefined } from '../undefined/index'
import { Uint8Array, type TUint8Array } from '../uint8array/index'
import { Unknown, type TUnknown } from '../unknown/index'
import { CreateType } from '../create/index'
// ------------------------------------------------------------------
// ValueGuard
// ------------------------------------------------------------------
import { IsArray, IsNumber, IsBigInt, IsUint8Array, IsDate, IsIterator, IsObject, IsAsyncIterator, IsFunction, IsUndefined, IsNull, IsSymbol, IsBoolean, IsString } from '../guard/value'
// ------------------------------------------------------------------
// FromArray
// ------------------------------------------------------------------
// prettier-ignore
type TFromArray<T extends readonly unknown[]> =
T extends readonly [infer L extends unknown, ...infer R extends unknown[]]
? [FromValue<L, false>, ...TFromArray<R>]
: T
// prettier-ignore
function FromArray<T extends readonly unknown[]>(T: [...T]): TFromArray<T> {
return T.map(L => FromValue(L, false)) as never
}
// ------------------------------------------------------------------
// FromProperties
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperties<T extends Record<PropertyKey, unknown>> = {
-readonly [K in keyof T]: FromValue<T[K], false> extends infer R extends TSchema
? TReadonly<R>
: TReadonly<TNever>
}
// prettier-ignore
function FromProperties<T extends Record<PropertyKey, unknown>>(value: T): TFromProperties<T> {
const Acc = {} as TProperties
for(const K of globalThis.Object.getOwnPropertyNames(value)) Acc[K] = Readonly(FromValue(value[K], false))
return Acc as never
}
// ------------------------------------------------------------------
// ConditionalReadonly - Only applied if not root
// ------------------------------------------------------------------
type TConditionalReadonly<T extends TSchema, Root extends boolean> = Root extends true ? T : TReadonly<T>
function ConditionalReadonly<T extends TSchema, Root extends boolean>(T: T, root: Root): TConditionalReadonly<T, Root> {
return (root === true ? T : Readonly(T)) as never
}
// ------------------------------------------------------------------
// FromValue
// ------------------------------------------------------------------
// prettier-ignore
type FromValue<T, Root extends boolean> =
T extends AsyncIterableIterator<unknown> ? TConditionalReadonly<TAny, Root> :
T extends IterableIterator<unknown> ? TConditionalReadonly<TAny, Root> :
T extends readonly unknown[] ? TReadonly<TTuple<AssertRest<TFromArray<T>>>> :
T extends Uint8Array ? TUint8Array :
T extends Date ? TDate :
T extends Record<PropertyKey, unknown> ? TConditionalReadonly<TObject<Evaluate<TFromProperties<T>>>, Root> :
T extends Function ? TConditionalReadonly<TFunction<[], TUnknown>, Root> :
T extends undefined ? TUndefined :
T extends null ? TNull :
T extends symbol ? TSymbol :
T extends number ? TLiteral<T> :
T extends boolean ? TLiteral<T> :
T extends string ? TLiteral<T> :
T extends bigint ? TBigInt :
TObject<{}>
// prettier-ignore
function FromValue<T, Root extends boolean>(value: T, root: Root): FromValue<T, Root> {
return (
IsAsyncIterator(value) ? ConditionalReadonly(Any(), root) :
IsIterator(value) ? ConditionalReadonly(Any(), root) :
IsArray(value) ? Readonly(Tuple(FromArray(value) as TSchema[])) :
IsUint8Array(value) ? Uint8Array() :
IsDate(value) ? Date() :
IsObject(value) ? ConditionalReadonly(Object(FromProperties(value as Record<PropertyKey, unknown>) as TProperties), root) :
IsFunction(value) ? ConditionalReadonly(FunctionType([], Unknown()), root) :
IsUndefined(value) ? Undefined() :
IsNull(value) ? Null() :
IsSymbol(value) ? Symbol() :
IsBigInt(value) ? BigInt() :
IsNumber(value) ? Literal(value) :
IsBoolean(value) ? Literal(value) :
IsString(value) ? Literal(value) :
Object({})
) as never
}
// ------------------------------------------------------------------
// TConst
// ------------------------------------------------------------------
export type TConst<T> = FromValue<T, true>
/** `[JavaScript]` Creates a readonly const type from the given value. */
export function Const</* const (not supported in 4.0) */ T>(T: T, options?: SchemaOptions): TConst<T> {
return CreateType(FromValue(T, true), options) as never
}

29
src/type/const/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './const'

View File

@@ -0,0 +1,47 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import type { TConstructor } from '../constructor/index'
import { Tuple, type TTuple } from '../tuple/index'
import { Never, type TNever } from '../never/index'
import * as KindGuard from '../guard/kind'
// ------------------------------------------------------------------
// ConstructorParameters
// ------------------------------------------------------------------
// prettier-ignore
export type TConstructorParameters<Type extends TSchema> = (
Type extends TConstructor<infer Parameters extends TSchema[], infer _InstanceType extends TSchema>
? TTuple<Parameters>
: TNever
)
/** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */
export function ConstructorParameters<Type extends TSchema>(schema: Type, options?: SchemaOptions): TConstructorParameters<Type> {
return (KindGuard.IsConstructor(schema) ? Tuple(schema.parameters, options) : Never(options)) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './constructor-parameters'

View File

@@ -0,0 +1,70 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import type { Static } from '../static/index'
import type { Ensure } from '../helpers/index'
import type { TReadonlyOptional } from '../readonly-optional/index'
import type { TReadonly } from '../readonly/index'
import type { TOptional } from '../optional/index'
import { CreateType } from '../create/type'
import { Kind } from '../symbols/index'
// ------------------------------------------------------------------
// StaticConstructor
// ------------------------------------------------------------------
type StaticReturnType<U extends TSchema, P extends unknown[]> = Static<U, P>
// prettier-ignore
type StaticParameter<T extends TSchema, P extends unknown[]> =
T extends TReadonlyOptional<T> ? [Readonly<Static<T, P>>?] :
T extends TReadonly<T> ? [Readonly<Static<T, P>>] :
T extends TOptional<T> ? [Static<T, P>?] :
[Static<T, P>]
// prettier-ignore
type StaticParameters<T extends TSchema[], P extends unknown[], Acc extends unknown[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? StaticParameters<R, P, [...Acc, ...StaticParameter<L, P>]>
: Acc
)
// prettier-ignore
type StaticConstructor<T extends TSchema[], U extends TSchema, P extends unknown[]> =
Ensure<new (...param: StaticParameters<T, P>) => StaticReturnType<U, P>>
// ------------------------------------------------------------------
// TConstructor
// ------------------------------------------------------------------
export interface TConstructor<T extends TSchema[] = TSchema[], U extends TSchema = TSchema> extends TSchema {
[Kind]: 'Constructor'
static: StaticConstructor<T, U, this['params']>
type: 'Constructor'
parameters: T
returns: U
}
/** `[JavaScript]` Creates a Constructor type */
export function Constructor<T extends TSchema[], U extends TSchema>(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor<T, U> {
return CreateType({ [Kind]: 'Constructor', type: 'Constructor', parameters, returns }, options) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './constructor'

View File

@@ -0,0 +1,65 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 ValueGuard from '../guard/value'
function ImmutableArray(value: unknown[]) {
return globalThis.Object.freeze(value as any).map((value: unknown) => Immutable(value as any))
}
function ImmutableDate(value: Date) {
return value
}
function ImmutableUint8Array(value: Uint8Array) {
return value
}
function ImmutableRegExp(value: RegExp) {
return value
}
function ImmutableObject(value: Record<keyof any, unknown>) {
const result = {} as Record<PropertyKey, unknown>
for (const key of Object.getOwnPropertyNames(value)) {
result[key] = Immutable(value[key])
}
for (const key of Object.getOwnPropertySymbols(value)) {
result[key] = Immutable(value[key])
}
return globalThis.Object.freeze(result)
}
/** Specialized deep immutable value. Applies freeze recursively to the given value */
// prettier-ignore
export function Immutable(value: unknown): unknown {
return (
ValueGuard.IsArray(value) ? ImmutableArray(value) :
ValueGuard.IsDate(value) ? ImmutableDate(value) :
ValueGuard.IsUint8Array(value) ? ImmutableUint8Array(value) :
ValueGuard.IsRegExp(value) ? ImmutableRegExp(value) :
ValueGuard.IsObject(value) ? ImmutableObject(value) :
value
)
}

29
src/type/create/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './type'

45
src/type/create/type.ts Normal file
View File

@@ -0,0 +1,45 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { TypeSystemPolicy } from '../../system/policy'
import { SchemaOptions } from '../schema/schema'
import { Immutable } from './immutable'
import { Clone } from '../clone/value'
/** Creates TypeBox schematics using the configured InstanceMode */
export function CreateType(schema: Record<any, unknown>, options?: SchemaOptions): unknown {
const result = options !== undefined ? { ...options, ...schema } : schema
switch (TypeSystemPolicy.InstanceMode) {
case 'freeze':
return Immutable(result)
case 'clone':
return Clone(result)
default:
return result
}
}

53
src/type/date/date.ts Normal file
View File

@@ -0,0 +1,53 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'
import { CreateType } from '../create/type'
export interface DateOptions extends SchemaOptions {
/** The exclusive maximum timestamp value */
exclusiveMaximumTimestamp?: number
/** The exclusive minimum timestamp value */
exclusiveMinimumTimestamp?: number
/** The maximum timestamp value */
maximumTimestamp?: number
/** The minimum timestamp value */
minimumTimestamp?: number
/** The multiple of timestamp value */
multipleOfTimestamp?: number
}
export interface TDate extends TSchema, DateOptions {
[Kind]: 'Date'
static: Date
type: 'date'
}
/** `[JavaScript]` Creates a Date type */
export function Date(options?: DateOptions): TDate {
return CreateType({ [Kind]: 'Date', type: 'Date' }, options) as never
}

29
src/type/date/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './date'

View File

@@ -0,0 +1,36 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
function DiscardKey(value: Record<PropertyKey, any>, key: PropertyKey) {
const { [key]: _, ...rest } = value
return rest
}
/** Discards property keys from the given value. This function returns a shallow Clone. */
export function Discard(value: Record<PropertyKey, any>, keys: PropertyKey[]) {
return keys.reduce((acc, key) => DiscardKey(acc, key), value)
}

29
src/type/discard/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './discard'

58
src/type/enum/enum.ts Normal file
View File

@@ -0,0 +1,58 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import { Literal, type TLiteral } from '../literal/index'
import { Kind, Hint } from '../symbols/index'
import { Union } from '../union/index'
// ------------------------------------------------------------------
// ValueGuard
// ------------------------------------------------------------------
import { IsUndefined } from '../guard/value'
// ------------------------------------------------------------------
// TEnum
// ------------------------------------------------------------------
export type TEnumRecord = Record<TEnumKey, TEnumValue>
export type TEnumValue = string | number
export type TEnumKey = string
export interface TEnum<T extends Record<string, string | number> = Record<string, string | number>> extends TSchema {
[Kind]: 'Union'
[Hint]: 'Enum'
static: T[keyof T]
anyOf: TLiteral<T[keyof T]>[]
}
/** `[Json]` Creates a Enum type */
export function Enum<V extends TEnumValue, T extends Record<TEnumKey, V>>(item: T, options?: SchemaOptions): TEnum<T> {
if (IsUndefined(item)) throw new Error('Enum undefined or empty')
const values1 = globalThis.Object.getOwnPropertyNames(item)
.filter((key) => isNaN(key as any))
.map((key) => item[key]) as T[keyof T][]
const values2 = [...new Set(values1)]
const anyOf = values2.map((value) => Literal(value))
return Union(anyOf, { ...options, [Hint]: 'Enum' }) as never
}

29
src/type/enum/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './enum'

34
src/type/error/error.ts Normal file
View File

@@ -0,0 +1,34 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
/** The base Error type thrown for all TypeBox exceptions */
export class TypeBoxError extends Error {
constructor(message: string) {
super(message)
}
}

29
src/type/error/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './error'

View File

@@ -0,0 +1,89 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema } from '../schema/index'
import type { TProperties } from '../object/index'
import { MappedResult, type TMappedResult } from '../mapped/index'
import { Exclude, type TExclude } from './exclude'
// ------------------------------------------------------------------
// FromProperties
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperties<
K extends TProperties,
T extends TSchema
> = (
{ [K2 in keyof K]: TExclude<K[K2], T> }
)
// prettier-ignore
function FromProperties<
P extends TProperties,
T extends TSchema
>(P: P, U: T): TFromProperties<P, T> {
const Acc = {} as TProperties
for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Exclude(P[K2], U)
return Acc as never
}
// ------------------------------------------------------------------
// FromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
type TFromMappedResult<
R extends TMappedResult,
T extends TSchema
> = (
TFromProperties<R['properties'], T>
)
// prettier-ignore
function FromMappedResult<
R extends TMappedResult,
T extends TSchema
>(R: R, T: T): TFromMappedResult<R, T> {
return FromProperties(R.properties, T) as never
}
// ------------------------------------------------------------------
// ExcludeFromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
export type TExcludeFromMappedResult<
R extends TMappedResult,
T extends TSchema,
P extends TProperties = TFromMappedResult<R, T>
> = (
TMappedResult<P>
)
// prettier-ignore
export function ExcludeFromMappedResult<
R extends TMappedResult,
T extends TSchema,
P extends TProperties = TFromMappedResult<R, T>
>(R: R, T: T): TMappedResult<P> {
const P = FromMappedResult(R, T)
return MappedResult(P) as never
}

View File

@@ -0,0 +1,39 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema } from '../schema/index'
import { TExclude, Exclude } from './exclude'
import { TemplateLiteralToUnion, type TTemplateLiteral, type TTemplateLiteralToUnion } from '../template-literal/index'
// prettier-ignore
export type TExcludeFromTemplateLiteral<L extends TTemplateLiteral, R extends TSchema> = (
TExclude<TTemplateLiteralToUnion<L>, R>
)
export function ExcludeFromTemplateLiteral<L extends TTemplateLiteral, R extends TSchema>(L: L, R: R): TExcludeFromTemplateLiteral<L, R> {
return Exclude(TemplateLiteralToUnion(L), R) as never
}

View File

@@ -0,0 +1,82 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { UnionToTuple, AssertRest, AssertType } from '../helpers/index'
import type { TMappedResult } from '../mapped/index'
import { type TTemplateLiteral } from '../template-literal/index'
import { Union, type TUnion } from '../union/index'
import { Never, type TNever } from '../never/index'
import { type Static } from '../static/index'
import { type TUnionEvaluated } from '../union/index'
import { ExtendsCheck, ExtendsResult } from '../extends/index'
import { ExcludeFromMappedResult, type TExcludeFromMappedResult } from './exclude-from-mapped-result'
import { ExcludeFromTemplateLiteral, type TExcludeFromTemplateLiteral } from './exclude-from-template-literal'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/kind'
// ------------------------------------------------------------------
// ExcludeRest
// ------------------------------------------------------------------
// prettier-ignore
type TExcludeRest<L extends TSchema[], R extends TSchema> = AssertRest<UnionToTuple<{
[K in keyof L]: Static<AssertType<L[K]>> extends Static<R> ? never : L[K]
}[number]>> extends infer R extends TSchema[] ? TUnionEvaluated<R> : never
function ExcludeRest<L extends TSchema[], R extends TSchema>(L: [...L], R: R) {
const excluded = L.filter((inner) => ExtendsCheck(inner, R) === ExtendsResult.False)
return excluded.length === 1 ? excluded[0] : Union(excluded)
}
// ------------------------------------------------------------------
// TExclude
// ------------------------------------------------------------------
// prettier-ignore
export type TExclude<L extends TSchema, R extends TSchema> = (
L extends TUnion<infer S> ? TExcludeRest<S, R> :
L extends R ? TNever : L
)
/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */
export function Exclude<L extends TMappedResult, R extends TSchema>(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromMappedResult<L, R>
/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */
export function Exclude<L extends TTemplateLiteral, R extends TSchema>(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromTemplateLiteral<L, R>
/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */
export function Exclude<L extends TSchema, R extends TSchema>(unionType: L, excludedMembers: R, options?: SchemaOptions): TExclude<L, R>
/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */
export function Exclude(L: TSchema, R: TSchema, options: SchemaOptions = {}): any {
// overloads
if (IsTemplateLiteral(L)) return CreateType(ExcludeFromTemplateLiteral(L, R), options)
if (IsMappedResult(L)) return CreateType(ExcludeFromMappedResult(L, R), options)
// prettier-ignore
return CreateType(
IsUnion(L) ? ExcludeRest(L.anyOf, R) :
ExtendsCheck(L, R) !== ExtendsResult.False ? Never() : L
, options)
}

31
src/type/exclude/index.ts Normal file
View File

@@ -0,0 +1,31 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './exclude-from-mapped-result'
export * from './exclude-from-template-literal'
export * from './exclude'

View File

@@ -0,0 +1,776 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { type TAny, Any } from '../any/index'
import { type TArray } from '../array/index'
import { type TAsyncIterator } from '../async-iterator/index'
import { type TBigInt } from '../bigint/index'
import { type TBoolean } from '../boolean/index'
import { type TConstructor } from '../constructor/index'
import { type TDate } from '../date/index'
import { type TFunction, Function as FunctionType } from '../function/index'
import { type TInteger } from '../integer/index'
import { type TIntersect } from '../intersect/index'
import { type TIterator } from '../iterator/index'
import { type TLiteral } from '../literal/index'
import { type TNever } from '../never/index'
import { type TNot } from '../not/index'
import { type TNull } from '../null/index'
import { type TNumber, Number } from '../number/index'
import { type TObject } from '../object/index'
import { type TPromise } from '../promise/index'
import { type TRecord } from '../record/index'
import { type TSchema } from '../schema/index'
import { type TString, String } from '../string/index'
import { type TSymbol } from '../symbol/index'
import { type TTuple } from '../tuple/index'
import { type TUint8Array } from '../uint8array/index'
import { type TUndefined } from '../undefined/index'
import { type TUnion } from '../union/index'
import { type TUnknown, Unknown } from '../unknown/index'
import { type TVoid } from '../void/index'
import { TemplateLiteralToUnion } from '../template-literal/index'
import { PatternNumberExact, PatternStringExact } from '../patterns/index'
import { Kind, Hint } from '../symbols/index'
import { TypeBoxError } from '../error/index'
import { TypeGuard, ValueGuard } from '../guard/index'
export class ExtendsResolverError extends TypeBoxError {}
export enum ExtendsResult {
Union,
True,
False,
}
// ------------------------------------------------------------------
// IntoBooleanResult
// ------------------------------------------------------------------
// prettier-ignore
function IntoBooleanResult(result: ExtendsResult) {
return result === ExtendsResult.False ? result : ExtendsResult.True
}
// ------------------------------------------------------------------
// Throw
// ------------------------------------------------------------------
// prettier-ignore
function Throw(message: string): never {
throw new ExtendsResolverError(message)
}
// ------------------------------------------------------------------
// StructuralRight
// ------------------------------------------------------------------
// prettier-ignore
function IsStructuralRight(right: TSchema): boolean {
return (
TypeGuard.IsNever(right) ||
TypeGuard.IsIntersect(right) ||
TypeGuard.IsUnion(right) ||
TypeGuard.IsUnknown(right) ||
TypeGuard.IsAny(right)
)
}
// prettier-ignore
function StructuralRight(left: TSchema, right: TSchema) {
return (
TypeGuard.IsNever(right) ? FromNeverRight(left, right) :
TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) :
TypeGuard.IsUnion(right) ? FromUnionRight(left, right) :
TypeGuard.IsUnknown(right) ? FromUnknownRight(left, right) :
TypeGuard.IsAny(right) ? FromAnyRight(left, right) :
Throw('StructuralRight')
)
}
// ------------------------------------------------------------------
// Any
// ------------------------------------------------------------------
// prettier-ignore
function FromAnyRight(left: TSchema, right: TAny) {
return ExtendsResult.True
}
// prettier-ignore
function FromAny(left: TAny, right: TSchema) {
return (
TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) :
(TypeGuard.IsUnion(right) && right.anyOf.some((schema) => TypeGuard.IsAny(schema) || TypeGuard.IsUnknown(schema))) ? ExtendsResult.True :
TypeGuard.IsUnion(right) ? ExtendsResult.Union :
TypeGuard.IsUnknown(right) ? ExtendsResult.True :
TypeGuard.IsAny(right) ? ExtendsResult.True :
ExtendsResult.Union
)
}
// ------------------------------------------------------------------
// Array
// ------------------------------------------------------------------
// prettier-ignore
function FromArrayRight(left: TSchema, right: TArray) {
return (
TypeGuard.IsUnknown(left) ? ExtendsResult.False :
TypeGuard.IsAny(left) ? ExtendsResult.Union :
TypeGuard.IsNever(left) ? ExtendsResult.True :
ExtendsResult.False
)
}
// prettier-ignore
function FromArray(left: TArray, right: TSchema) {
return (
TypeGuard.IsObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True :
IsStructuralRight(right) ? StructuralRight(left, right) :
!TypeGuard.IsArray(right) ? ExtendsResult.False :
IntoBooleanResult(Visit(left.items, right.items))
)
}
// ------------------------------------------------------------------
// AsyncIterator
// ------------------------------------------------------------------
// prettier-ignore
function FromAsyncIterator(left: TAsyncIterator, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
!TypeGuard.IsAsyncIterator(right) ? ExtendsResult.False :
IntoBooleanResult(Visit(left.items, right.items))
)
}
// ------------------------------------------------------------------
// BigInt
// ------------------------------------------------------------------
// prettier-ignore
function FromBigInt(left: TBigInt, right: TSchema): ExtendsResult {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsBigInt(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Boolean
// ------------------------------------------------------------------
// prettier-ignore
function FromBooleanRight(left: TSchema, right: TBoolean) {
return (
TypeGuard.IsLiteralBoolean(left) ? ExtendsResult.True :
TypeGuard.IsBoolean(left) ? ExtendsResult.True :
ExtendsResult.False
)
}
// prettier-ignore
function FromBoolean(left: TBoolean, right: TSchema): ExtendsResult {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsBoolean(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------
// prettier-ignore
function FromConstructor(left: TConstructor, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
!TypeGuard.IsConstructor(right) ? ExtendsResult.False :
left.parameters.length > right.parameters.length ? ExtendsResult.False :
(!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === ExtendsResult.True)) ? ExtendsResult.False :
IntoBooleanResult(Visit(left.returns, right.returns))
)
}
// ------------------------------------------------------------------
// Date
// ------------------------------------------------------------------
// prettier-ignore
function FromDate(left: TDate, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsDate(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Function
// ------------------------------------------------------------------
// prettier-ignore
function FromFunction(left: TFunction, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
!TypeGuard.IsFunction(right) ? ExtendsResult.False :
left.parameters.length > right.parameters.length ? ExtendsResult.False :
(!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === ExtendsResult.True)) ? ExtendsResult.False :
IntoBooleanResult(Visit(left.returns, right.returns))
)
}
// ------------------------------------------------------------------
// Integer
// ------------------------------------------------------------------
// prettier-ignore
function FromIntegerRight(left: TSchema, right: TInteger) {
return (
TypeGuard.IsLiteral(left) && ValueGuard.IsNumber(left.const) ? ExtendsResult.True :
TypeGuard.IsNumber(left) || TypeGuard.IsInteger(left) ? ExtendsResult.True :
ExtendsResult.False
)
}
// prettier-ignore
function FromInteger(left: TInteger, right: TSchema): ExtendsResult {
return (
TypeGuard.IsInteger(right) || TypeGuard.IsNumber(right) ? ExtendsResult.True :
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Intersect
// ------------------------------------------------------------------
// prettier-ignore
function FromIntersectRight(left: TSchema, right: TIntersect): ExtendsResult {
return right.allOf.every((schema) => Visit(left, schema) === ExtendsResult.True)
? ExtendsResult.True
: ExtendsResult.False
}
// prettier-ignore
function FromIntersect(left: TIntersect, right: TSchema) {
return left.allOf.some((schema) => Visit(schema, right) === ExtendsResult.True)
? ExtendsResult.True
: ExtendsResult.False
}
// ------------------------------------------------------------------
// Iterator
// ------------------------------------------------------------------
// prettier-ignore
function FromIterator(left: TIterator, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
!TypeGuard.IsIterator(right) ? ExtendsResult.False :
IntoBooleanResult(Visit(left.items, right.items))
)
}
// ------------------------------------------------------------------
// Literal
// ------------------------------------------------------------------
// prettier-ignore
function FromLiteral(left: TLiteral, right: TSchema): ExtendsResult {
return (
TypeGuard.IsLiteral(right) && right.const === left.const ? ExtendsResult.True :
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsString(right) ? FromStringRight(left, right) :
TypeGuard.IsNumber(right) ? FromNumberRight(left, right) :
TypeGuard.IsInteger(right) ? FromIntegerRight(left, right) :
TypeGuard.IsBoolean(right) ? FromBooleanRight(left, right) :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Never
// ------------------------------------------------------------------
// prettier-ignore
function FromNeverRight(left: TSchema, right: TNever) {
return ExtendsResult.False
}
// prettier-ignore
function FromNever(left: TNever, right: TSchema) {
return ExtendsResult.True
}
// ------------------------------------------------------------------
// Not
// ------------------------------------------------------------------
// prettier-ignore
function UnwrapTNot<T extends TNot>(schema: T): TUnknown | TNot['not'] {
let [current, depth]: [TSchema, number] = [schema, 0]
while (true) {
if (!TypeGuard.IsNot(current)) break
current = current.not
depth += 1
}
return depth % 2 === 0 ? current : Unknown()
}
// prettier-ignore
function FromNot(left: TSchema, right: TSchema) {
// TypeScript has no concept of negated types, and attempts to correctly check the negated
// type at runtime would put TypeBox at odds with TypeScripts ability to statically infer
// the type. Instead we unwrap to either unknown or T and continue evaluating.
// prettier-ignore
return (
TypeGuard.IsNot(left) ? Visit(UnwrapTNot(left), right) :
TypeGuard.IsNot(right) ? Visit(left, UnwrapTNot(right)) :
Throw('Invalid fallthrough for Not')
)
}
// ------------------------------------------------------------------
// Null
// ------------------------------------------------------------------
// prettier-ignore
function FromNull(left: TNull, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsNull(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
// prettier-ignore
function FromNumberRight(left: TSchema, right: TNumber) {
return (
TypeGuard.IsLiteralNumber(left) ? ExtendsResult.True :
TypeGuard.IsNumber(left) || TypeGuard.IsInteger(left) ? ExtendsResult.True :
ExtendsResult.False
)
}
// prettier-ignore
function FromNumber(left: TNumber, right: TSchema): ExtendsResult {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsInteger(right) || TypeGuard.IsNumber(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Object
// ------------------------------------------------------------------
// prettier-ignore
function IsObjectPropertyCount(schema: TObject, count: number) {
return Object.getOwnPropertyNames(schema.properties).length === count
}
// prettier-ignore
function IsObjectStringLike(schema: TObject) {
return IsObjectArrayLike(schema)
}
// prettier-ignore
function IsObjectSymbolLike(schema: TObject) {
return IsObjectPropertyCount(schema, 0) || (
IsObjectPropertyCount(schema, 1) && 'description' in schema.properties && TypeGuard.IsUnion(schema.properties.description) && schema.properties.description.anyOf.length === 2 && ((
TypeGuard.IsString(schema.properties.description.anyOf[0]) &&
TypeGuard.IsUndefined(schema.properties.description.anyOf[1])
) || (
TypeGuard.IsString(schema.properties.description.anyOf[1]) &&
TypeGuard.IsUndefined(schema.properties.description.anyOf[0])
))
)
}
// prettier-ignore
function IsObjectNumberLike(schema: TObject) {
return IsObjectPropertyCount(schema, 0)
}
// prettier-ignore
function IsObjectBooleanLike(schema: TObject) {
return IsObjectPropertyCount(schema, 0)
}
// prettier-ignore
function IsObjectBigIntLike(schema: TObject) {
return IsObjectPropertyCount(schema, 0)
}
// prettier-ignore
function IsObjectDateLike(schema: TObject) {
return IsObjectPropertyCount(schema, 0)
}
// prettier-ignore
function IsObjectUint8ArrayLike(schema: TObject) {
return IsObjectArrayLike(schema)
}
// prettier-ignore
function IsObjectFunctionLike(schema: TObject) {
const length = Number()
return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === ExtendsResult.True)
}
// prettier-ignore
function IsObjectConstructorLike(schema: TObject) {
return IsObjectPropertyCount(schema, 0)
}
// prettier-ignore
function IsObjectArrayLike(schema: TObject) {
const length = Number()
return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === ExtendsResult.True)
}
// prettier-ignore
function IsObjectPromiseLike(schema: TObject) {
const then = FunctionType([Any()], Any())
return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'then' in schema.properties && IntoBooleanResult(Visit(schema.properties['then'], then)) === ExtendsResult.True)
}
// ------------------------------------------------------------------
// Property
// ------------------------------------------------------------------
// prettier-ignore
function Property(left: TSchema, right: TSchema) {
return (
Visit(left, right) === ExtendsResult.False ? ExtendsResult.False :
TypeGuard.IsOptional(left) && !TypeGuard.IsOptional(right) ? ExtendsResult.False :
ExtendsResult.True
)
}
// prettier-ignore
function FromObjectRight(left: TSchema, right: TObject) {
return (
TypeGuard.IsUnknown(left) ? ExtendsResult.False :
TypeGuard.IsAny(left) ? ExtendsResult.Union : (
TypeGuard.IsNever(left) ||
(TypeGuard.IsLiteralString(left) && IsObjectStringLike(right)) ||
(TypeGuard.IsLiteralNumber(left) && IsObjectNumberLike(right)) ||
(TypeGuard.IsLiteralBoolean(left) && IsObjectBooleanLike(right)) ||
(TypeGuard.IsSymbol(left) && IsObjectSymbolLike(right)) ||
(TypeGuard.IsBigInt(left) && IsObjectBigIntLike(right)) ||
(TypeGuard.IsString(left) && IsObjectStringLike(right)) ||
(TypeGuard.IsSymbol(left) && IsObjectSymbolLike(right)) ||
(TypeGuard.IsNumber(left) && IsObjectNumberLike(right)) ||
(TypeGuard.IsInteger(left) && IsObjectNumberLike(right)) ||
(TypeGuard.IsBoolean(left) && IsObjectBooleanLike(right)) ||
(TypeGuard.IsUint8Array(left) && IsObjectUint8ArrayLike(right)) ||
(TypeGuard.IsDate(left) && IsObjectDateLike(right)) ||
(TypeGuard.IsConstructor(left) && IsObjectConstructorLike(right)) ||
(TypeGuard.IsFunction(left) && IsObjectFunctionLike(right))
) ? ExtendsResult.True :
(TypeGuard.IsRecord(left) && TypeGuard.IsString(RecordKey(left))) ? (() => {
// When expressing a Record with literal key values, the Record is converted into a Object with
// the Hint assigned as `Record`. This is used to invert the extends logic.
return right[Hint] === 'Record' ? ExtendsResult.True : ExtendsResult.False
})() :
(TypeGuard.IsRecord(left) && TypeGuard.IsNumber(RecordKey(left))) ? (() => {
return IsObjectPropertyCount(right, 0) ? ExtendsResult.True : ExtendsResult.False
})() :
ExtendsResult.False
)
}
// prettier-ignore
function FromObject(left: TObject, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
!TypeGuard.IsObject(right) ? ExtendsResult.False :
(() => {
for (const key of Object.getOwnPropertyNames(right.properties)) {
if (!(key in left.properties) && !TypeGuard.IsOptional(right.properties[key])) {
return ExtendsResult.False
}
if (TypeGuard.IsOptional(right.properties[key])) {
return ExtendsResult.True
}
if (Property(left.properties[key], right.properties[key]) === ExtendsResult.False) {
return ExtendsResult.False
}
}
return ExtendsResult.True
})()
)
}
// ------------------------------------------------------------------
// Promise
// ------------------------------------------------------------------
// prettier-ignore
function FromPromise(left: TPromise, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) && IsObjectPromiseLike(right) ? ExtendsResult.True :
!TypeGuard.IsPromise(right) ? ExtendsResult.False :
IntoBooleanResult(Visit(left.item, right.item))
)
}
// ------------------------------------------------------------------
// Record
// ------------------------------------------------------------------
// prettier-ignore
function RecordKey(schema: TRecord) {
return (
PatternNumberExact in schema.patternProperties ? Number() :
PatternStringExact in schema.patternProperties ? String() :
Throw('Unknown record key pattern')
)
}
// prettier-ignore
function RecordValue(schema: TRecord) {
return (
PatternNumberExact in schema.patternProperties ? schema.patternProperties[PatternNumberExact] :
PatternStringExact in schema.patternProperties ? schema.patternProperties[PatternStringExact] :
Throw('Unable to get record value schema')
)
}
// prettier-ignore
function FromRecordRight(left: TSchema, right: TRecord) {
const [Key, Value] = [RecordKey(right), RecordValue(right)]
return (
(
TypeGuard.IsLiteralString(left) && TypeGuard.IsNumber(Key) && IntoBooleanResult(Visit(left, Value)) === ExtendsResult.True) ? ExtendsResult.True :
TypeGuard.IsUint8Array(left) && TypeGuard.IsNumber(Key) ? Visit(left, Value) :
TypeGuard.IsString(left) && TypeGuard.IsNumber(Key) ? Visit(left, Value) :
TypeGuard.IsArray(left) && TypeGuard.IsNumber(Key) ? Visit(left, Value) :
TypeGuard.IsObject(left) ? (() => {
for (const key of Object.getOwnPropertyNames(left.properties)) {
if (Property(Value, left.properties[key]) === ExtendsResult.False) {
return ExtendsResult.False
}
}
return ExtendsResult.True
})() :
ExtendsResult.False
)
}
// prettier-ignore
function FromRecord(left: TRecord, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
!TypeGuard.IsRecord(right) ? ExtendsResult.False :
Visit(RecordValue(left), RecordValue(right))
)
}
// ------------------------------------------------------------------
// RegExp
// ------------------------------------------------------------------
// prettier-ignore
function FromRegExp(left: TSchema, right: TSchema) {
// Note: RegExp types evaluate as strings, not RegExp objects.
// Here we remap either into string and continue evaluating.
const L = TypeGuard.IsRegExp(left) ? String() : left
const R = TypeGuard.IsRegExp(right) ? String() : right
return Visit(L, R)
}
// ------------------------------------------------------------------
// String
// ------------------------------------------------------------------
// prettier-ignore
function FromStringRight(left: TSchema, right: TString) {
return (
TypeGuard.IsLiteral(left) && ValueGuard.IsString(left.const) ? ExtendsResult.True :
TypeGuard.IsString(left) ? ExtendsResult.True :
ExtendsResult.False
)
}
// prettier-ignore
function FromString(left: TString, right: TSchema): ExtendsResult {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsString(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Symbol
// ------------------------------------------------------------------
// prettier-ignore
function FromSymbol(left: TSymbol, right: TSchema): ExtendsResult {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsSymbol(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// TemplateLiteral
// ------------------------------------------------------------------
// prettier-ignore
function FromTemplateLiteral(left: TSchema, right: TSchema) {
// TemplateLiteral types are resolved to either unions for finite expressions or string
// for infinite expressions. Here we call to TemplateLiteralResolver to resolve for
// either type and continue evaluating.
return (
TypeGuard.IsTemplateLiteral(left) ? Visit(TemplateLiteralToUnion(left), right) :
TypeGuard.IsTemplateLiteral(right) ? Visit(left, TemplateLiteralToUnion(right)) :
Throw('Invalid fallthrough for TemplateLiteral')
)
}
// ------------------------------------------------------------------
// Tuple
// ------------------------------------------------------------------
// prettier-ignore
function IsArrayOfTuple(left: TTuple, right: TSchema) {
return (
TypeGuard.IsArray(right) &&
left.items !== undefined &&
left.items.every((schema) => Visit(schema, right.items) === ExtendsResult.True)
)
}
// prettier-ignore
function FromTupleRight(left: TSchema, right: TTuple) {
return (
TypeGuard.IsNever(left) ? ExtendsResult.True :
TypeGuard.IsUnknown(left) ? ExtendsResult.False :
TypeGuard.IsAny(left) ? ExtendsResult.Union :
ExtendsResult.False
)
}
// prettier-ignore
function FromTuple(left: TTuple, right: TSchema): ExtendsResult {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True :
TypeGuard.IsArray(right) && IsArrayOfTuple(left, right) ? ExtendsResult.True :
!TypeGuard.IsTuple(right) ? ExtendsResult.False :
(ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) || (!ValueGuard.IsUndefined(left.items) && ValueGuard.IsUndefined(right.items)) ? ExtendsResult.False :
(ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) ? ExtendsResult.True :
left.items!.every((schema, index) => Visit(schema, right.items![index]) === ExtendsResult.True) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Uint8Array
// ------------------------------------------------------------------
// prettier-ignore
function FromUint8Array(left: TUint8Array, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsUint8Array(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Undefined
// ------------------------------------------------------------------
// prettier-ignore
function FromUndefined(left: TUndefined, right: TSchema) {
return (
IsStructuralRight(right) ? StructuralRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsRecord(right) ? FromRecordRight(left, right) :
TypeGuard.IsVoid(right) ? FromVoidRight(left, right) :
TypeGuard.IsUndefined(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
// prettier-ignore
function FromUnionRight(left: TSchema, right: TUnion): ExtendsResult {
return right.anyOf.some((schema) => Visit(left, schema) === ExtendsResult.True)
? ExtendsResult.True
: ExtendsResult.False
}
// prettier-ignore
function FromUnion(left: TUnion, right: TSchema): ExtendsResult {
return left.anyOf.every((schema) => Visit(schema, right) === ExtendsResult.True)
? ExtendsResult.True
: ExtendsResult.False
}
// ------------------------------------------------------------------
// Unknown
// ------------------------------------------------------------------
// prettier-ignore
function FromUnknownRight(left: TSchema, right: TUnknown) {
return ExtendsResult.True
}
// prettier-ignore
function FromUnknown(left: TUnknown, right: TSchema) {
return (
TypeGuard.IsNever(right) ? FromNeverRight(left, right) :
TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) :
TypeGuard.IsUnion(right) ? FromUnionRight(left, right) :
TypeGuard.IsAny(right) ? FromAnyRight(left, right) :
TypeGuard.IsString(right) ? FromStringRight(left, right) :
TypeGuard.IsNumber(right) ? FromNumberRight(left, right) :
TypeGuard.IsInteger(right) ? FromIntegerRight(left, right) :
TypeGuard.IsBoolean(right) ? FromBooleanRight(left, right) :
TypeGuard.IsArray(right) ? FromArrayRight(left, right) :
TypeGuard.IsTuple(right) ? FromTupleRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsUnknown(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// ------------------------------------------------------------------
// Void
// ------------------------------------------------------------------
// prettier-ignore
function FromVoidRight(left: TSchema, right: TVoid) {
return (
TypeGuard.IsUndefined(left) ? ExtendsResult.True :
TypeGuard.IsUndefined(left) ? ExtendsResult.True :
ExtendsResult.False
)
}
// prettier-ignore
function FromVoid(left: TVoid, right: TSchema) {
return (
TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) :
TypeGuard.IsUnion(right) ? FromUnionRight(left, right) :
TypeGuard.IsUnknown(right) ? FromUnknownRight(left, right) :
TypeGuard.IsAny(right) ? FromAnyRight(left, right) :
TypeGuard.IsObject(right) ? FromObjectRight(left, right) :
TypeGuard.IsVoid(right) ? ExtendsResult.True :
ExtendsResult.False
)
}
// prettier-ignore
function Visit(left: TSchema, right: TSchema): ExtendsResult {
return (
// resolvable
(TypeGuard.IsTemplateLiteral(left) || TypeGuard.IsTemplateLiteral(right)) ? FromTemplateLiteral(left, right) :
(TypeGuard.IsRegExp(left) || TypeGuard.IsRegExp(right)) ? FromRegExp(left, right) :
(TypeGuard.IsNot(left) || TypeGuard.IsNot(right)) ? FromNot(left, right) :
// standard
TypeGuard.IsAny(left) ? FromAny(left, right) :
TypeGuard.IsArray(left) ? FromArray(left, right) :
TypeGuard.IsBigInt(left) ? FromBigInt(left, right) :
TypeGuard.IsBoolean(left) ? FromBoolean(left, right) :
TypeGuard.IsAsyncIterator(left) ? FromAsyncIterator(left, right) :
TypeGuard.IsConstructor(left) ? FromConstructor(left, right) :
TypeGuard.IsDate(left) ? FromDate(left, right) :
TypeGuard.IsFunction(left) ? FromFunction(left, right) :
TypeGuard.IsInteger(left) ? FromInteger(left, right) :
TypeGuard.IsIntersect(left) ? FromIntersect(left, right) :
TypeGuard.IsIterator(left) ? FromIterator(left, right) :
TypeGuard.IsLiteral(left) ? FromLiteral(left, right) :
TypeGuard.IsNever(left) ? FromNever(left, right) :
TypeGuard.IsNull(left) ? FromNull(left, right) :
TypeGuard.IsNumber(left) ? FromNumber(left, right) :
TypeGuard.IsObject(left) ? FromObject(left, right) :
TypeGuard.IsRecord(left) ? FromRecord(left, right) :
TypeGuard.IsString(left) ? FromString(left, right) :
TypeGuard.IsSymbol(left) ? FromSymbol(left, right) :
TypeGuard.IsTuple(left) ? FromTuple(left, right) :
TypeGuard.IsPromise(left) ? FromPromise(left, right) :
TypeGuard.IsUint8Array(left) ? FromUint8Array(left, right) :
TypeGuard.IsUndefined(left) ? FromUndefined(left, right) :
TypeGuard.IsUnion(left) ? FromUnion(left, right) :
TypeGuard.IsUnknown(left) ? FromUnknown(left, right) :
TypeGuard.IsVoid(left) ? FromVoid(left, right) :
Throw(`Unknown left type operand '${left[Kind]}'`)
)
}
export function ExtendsCheck(left: TSchema, right: TSchema): ExtendsResult {
return Visit(left, right)
}

View File

@@ -0,0 +1,130 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import type { TProperties } from '../object/index'
import type { Assert } from '../helpers/index'
import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index'
import { Literal, type TLiteral, type TLiteralValue } from '../literal/index'
import { Extends, type TExtends } from './extends'
import { Clone } from '../clone/value'
// ------------------------------------------------------------------
// FromPropertyKey
// ------------------------------------------------------------------
// prettier-ignore
type TFromPropertyKey<
K extends PropertyKey,
U extends TSchema,
L extends TSchema,
R extends TSchema
> = {
[_ in K]: TExtends<TLiteral<Assert<K, TLiteralValue>>, U, L, R>
}
// prettier-ignore
function FromPropertyKey<
K extends PropertyKey,
U extends TSchema,
L extends TSchema,
R extends TSchema
>(K: K, U: U, L: L, R: R, options?: SchemaOptions): TFromPropertyKey<K, U, L, R> {
return {
[K]: Extends(Literal(K as TLiteralValue), U, L, R, Clone(options)) as any
} as never
}
// ------------------------------------------------------------------
// FromPropertyKeys
// ------------------------------------------------------------------
// prettier-ignore
type TFromPropertyKeys<
K extends PropertyKey[],
U extends TSchema,
L extends TSchema,
R extends TSchema,
Acc extends TProperties = {}
> = (
K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]]
? TFromPropertyKeys<RK, U, L, R, Acc & TFromPropertyKey<LK, U, L, R>>
: Acc
)
// prettier-ignore
function FromPropertyKeys<
K extends PropertyKey[],
U extends TSchema,
L extends TSchema,
R extends TSchema
>(K: [...K], U: U, L: L, R: R, options?: SchemaOptions): TFromPropertyKeys<K, U, L, R> {
return K.reduce((Acc, LK) => {
return { ...Acc, ...FromPropertyKey(LK, U, L, R, options) }
}, {} as TProperties) as never
}
// ------------------------------------------------------------------
// FromMappedKey
// ------------------------------------------------------------------
// prettier-ignore
type TFromMappedKey<
K extends TMappedKey,
U extends TSchema,
L extends TSchema,
R extends TSchema
> = (
TFromPropertyKeys<K['keys'], U, L, R>
)
// prettier-ignore
function FromMappedKey<
K extends TMappedKey,
U extends TSchema,
L extends TSchema,
R extends TSchema
>(K: K, U: U, L: L, R: R, options?: SchemaOptions): TFromMappedKey<K, U, L, R> {
return FromPropertyKeys(K.keys, U, L, R, options) as never
}
// ------------------------------------------------------------------
// ExtendsFromMappedKey
// ------------------------------------------------------------------
// prettier-ignore
export type TExtendsFromMappedKey<
T extends TMappedKey,
U extends TSchema,
L extends TSchema,
R extends TSchema,
P extends TProperties = TFromMappedKey<T, U, L, R>
> = (
TMappedResult<P>
)
// prettier-ignore
export function ExtendsFromMappedKey<
T extends TMappedKey,
U extends TSchema,
L extends TSchema,
R extends TSchema,
P extends TProperties = TFromMappedKey<T, U, L, R>
>(T: T, U: U, L: L, R: R, options?: SchemaOptions): TMappedResult<P> {
const P = FromMappedKey(T, U, L, R, options)
return MappedResult(P) as never
}

View File

@@ -0,0 +1,102 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import type { TProperties } from '../object/index'
import { MappedResult, type TMappedResult } from '../mapped/index'
import { Extends, type TExtends } from './extends'
import { Clone } from '../clone/value'
// ------------------------------------------------------------------
// FromProperties
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperties<
P extends TProperties,
Right extends TSchema,
False extends TSchema,
True extends TSchema
> = (
{ [K2 in keyof P]: TExtends<P[K2], Right, False, True> }
)
// prettier-ignore
function FromProperties<
P extends TProperties,
Right extends TSchema,
True extends TSchema,
False extends TSchema
>(P: P, Right: Right, True: True, False: False, options?: SchemaOptions): TFromProperties<P, Right, True, False> {
const Acc = {} as TProperties
for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Extends(P[K2], Right, True, False, Clone(options))
return Acc as never
}
// ------------------------------------------------------------------
// FromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
type TFromMappedResult<
Left extends TMappedResult,
Right extends TSchema,
True extends TSchema,
False extends TSchema
> = (
TFromProperties<Left['properties'], Right, True, False>
)
// prettier-ignore
function FromMappedResult<
Left extends TMappedResult,
Right extends TSchema,
True extends TSchema,
False extends TSchema
>(Left: Left, Right: Right, True: True, False: False, options?: SchemaOptions): TFromMappedResult<Left, Right, True, False> {
return FromProperties(Left.properties, Right, True, False, options) as never
}
// ------------------------------------------------------------------
// ExtendsFromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
export type TExtendsFromMappedResult<
Left extends TMappedResult,
Right extends TSchema,
True extends TSchema,
False extends TSchema,
P extends TProperties = TFromMappedResult<Left, Right, True, False>
> = (
TMappedResult<P>
)
// prettier-ignore
export function ExtendsFromMappedResult<
Left extends TMappedResult,
Right extends TSchema,
True extends TSchema,
False extends TSchema,
P extends TProperties = TFromMappedResult<Left, Right, True, False>
>(Left: Left, Right: Right, True: True, False: False, options?: SchemaOptions): TMappedResult<P> {
const P = FromMappedResult(Left, Right, True, False, options)
return MappedResult(P) as never
}

View File

@@ -0,0 +1,55 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema } from '../schema/index'
import type { TIntersect } from '../intersect/index'
import type { TUnion } from '../union/index'
import type { TNot } from '../not/index'
import { Kind } from '../symbols/index'
/** Fast undefined check used for properties of type undefined */
function Intersect(schema: TIntersect) {
return schema.allOf.every((schema) => ExtendsUndefinedCheck(schema))
}
function Union(schema: TUnion) {
return schema.anyOf.some((schema) => ExtendsUndefinedCheck(schema))
}
function Not(schema: TNot) {
return !ExtendsUndefinedCheck(schema.not)
}
/** Fast undefined check used for properties of type undefined */
// prettier-ignore
export function ExtendsUndefinedCheck(schema: TSchema): boolean {
return (
schema[Kind] === 'Intersect' ? Intersect(schema as TIntersect) :
schema[Kind] === 'Union' ? Union(schema as TUnion) :
schema[Kind] === 'Not' ? Not(schema as TNot) :
schema[Kind] === 'Undefined' ? true :
false
)
}

View File

@@ -0,0 +1,80 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { Static } from '../static/index'
import { type TUnion, Union } from '../union/index'
import { TMappedKey, TMappedResult } from '../mapped/index'
import { ExtendsCheck, ExtendsResult } from './extends-check'
import { UnionToTuple } from '../helpers/index'
import { ExtendsFromMappedKey, type TExtendsFromMappedKey } from './extends-from-mapped-key'
import { ExtendsFromMappedResult, type TExtendsFromMappedResult } from './extends-from-mapped-result'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsMappedKey, IsMappedResult } from '../guard/kind'
// prettier-ignore
type TExtendsResolve<L extends TSchema, R extends TSchema, T extends TSchema, U extends TSchema> = (
(Static<L> extends Static<R> ? T : U) extends infer O extends TSchema ?
UnionToTuple<O> extends [infer X extends TSchema, infer Y extends TSchema]
? TUnion<[X, Y]>
: O
: never
)
// prettier-ignore
function ExtendsResolve<L extends TSchema, R extends TSchema, T extends TSchema, U extends TSchema>(left: L, right: R, trueType: T, falseType: U): TExtendsResolve<L, R, T, U> {
const R = ExtendsCheck(left, right)
return (
R === ExtendsResult.Union ? Union([trueType, falseType]) :
R === ExtendsResult.True ? trueType :
falseType
) as never
}
// ------------------------------------------------------------------
// TExtends
// ------------------------------------------------------------------
export type TExtends<L extends TSchema, R extends TSchema, T extends TSchema, F extends TSchema> = TExtendsResolve<L, R, T, F>
/** `[Json]` Creates a Conditional type */
export function Extends<L extends TMappedResult, R extends TSchema, T extends TSchema, F extends TSchema>(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtendsFromMappedResult<L, R, T, F>
/** `[Json]` Creates a Conditional type */
export function Extends<L extends TMappedKey, R extends TSchema, T extends TSchema, F extends TSchema>(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtendsFromMappedKey<L, R, T, F>
/** `[Json]` Creates a Conditional type */
export function Extends<L extends TSchema, R extends TSchema, T extends TSchema, F extends TSchema>(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtends<L, R, T, F>
/** `[Json]` Creates a Conditional type */
export function Extends<L extends TSchema, R extends TSchema, T extends TSchema, F extends TSchema>(L: L, R: R, T: T, F: F, options?: SchemaOptions) {
// prettier-ignore
return (
IsMappedResult(L) ? ExtendsFromMappedResult(L, R, T, F, options) :
IsMappedKey(L) ? CreateType(ExtendsFromMappedKey(L, R, T, F, options)) :
CreateType(ExtendsResolve(L, R, T, F), options)
) as never
}

33
src/type/extends/index.ts Normal file
View File

@@ -0,0 +1,33 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './extends-check'
export * from './extends-from-mapped-key'
export * from './extends-from-mapped-result'
export * from './extends-undefined'
export * from './extends'

View File

@@ -0,0 +1,89 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema } from '../schema/index'
import type { TProperties } from '../object/index'
import { MappedResult, type TMappedResult } from '../mapped/index'
import { Extract, type TExtract } from './extract'
// ------------------------------------------------------------------
// FromProperties
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperties<
P extends TProperties,
T extends TSchema
> = (
{ [K2 in keyof P]: TExtract<P[K2], T> }
)
// prettier-ignore
function FromProperties<
P extends TProperties,
T extends TSchema
>(P: P, T: T): TFromProperties<P, T> {
const Acc = {} as TProperties
for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Extract(P[K2], T)
return Acc as never
}
// ------------------------------------------------------------------
// FromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
type TFromMappedResult<
R extends TMappedResult,
T extends TSchema
> = (
TFromProperties<R['properties'], T>
)
// prettier-ignore
function FromMappedResult<
R extends TMappedResult,
T extends TSchema
>(R: R, T: T): TFromMappedResult<R, T> {
return FromProperties(R.properties, T) as never
}
// ------------------------------------------------------------------
// ExtractFromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
export type TExtractFromMappedResult<
R extends TMappedResult,
T extends TSchema,
P extends TProperties = TFromMappedResult<R, T>
> = (
TMappedResult<P>
)
// prettier-ignore
export function ExtractFromMappedResult<
R extends TMappedResult,
T extends TSchema,
P extends TProperties = TFromMappedResult<R, T>
>(R: R, T: T): TMappedResult<P> {
const P = FromMappedResult(R, T)
return MappedResult(P) as never
}

View File

@@ -0,0 +1,39 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema } from '../schema/index'
import { Extract, type TExtract } from './extract'
import { TemplateLiteralToUnion, type TTemplateLiteral, type TTemplateLiteralToUnion } from '../template-literal/index'
// prettier-ignore
export type TExtractFromTemplateLiteral<L extends TTemplateLiteral, R extends TSchema> = (
TExtract<TTemplateLiteralToUnion<L>, R>
)
export function ExtractFromTemplateLiteral<L extends TTemplateLiteral, R extends TSchema>(L: L, R: R): TExtractFromTemplateLiteral<L, R> {
return Extract(TemplateLiteralToUnion(L), R) as never
}

View File

@@ -0,0 +1,83 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { AssertRest, AssertType, UnionToTuple } from '../helpers/index'
import type { TMappedResult } from '../mapped/index'
import { Union, type TUnion } from '../union/index'
import { type Static } from '../static/index'
import { Never, type TNever } from '../never/index'
import { type TUnionEvaluated } from '../union/index'
import { type TTemplateLiteral } from '../template-literal/index'
import { ExtendsCheck, ExtendsResult } from '../extends/index'
import { ExtractFromMappedResult, type TExtractFromMappedResult } from './extract-from-mapped-result'
import { ExtractFromTemplateLiteral, type TExtractFromTemplateLiteral } from './extract-from-template-literal'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/kind'
// ------------------------------------------------------------------
// ExtractRest
// ------------------------------------------------------------------
// prettier-ignore
type TExtractRest<L extends TSchema[], R extends TSchema> = AssertRest<UnionToTuple<
{ [K in keyof L]: Static<AssertType<L[K]>> extends Static<R> ? L[K] : never
}[number]>> extends infer R extends TSchema[] ? TUnionEvaluated<R> : never
function ExtractRest<L extends TSchema[], R extends TSchema>(L: [...L], R: R) {
const extracted = L.filter((inner) => ExtendsCheck(inner, R) !== ExtendsResult.False)
return extracted.length === 1 ? extracted[0] : Union(extracted)
}
// ------------------------------------------------------------------
// TExtract
// ------------------------------------------------------------------
// prettier-ignore
export type TExtract<L extends TSchema, U extends TSchema> = (
L extends TUnion<infer S> ? TExtractRest<S, U> :
L extends U ? L : TNever
)
/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */
export function Extract<L extends TMappedResult, R extends TSchema>(type: L, union: R, options?: SchemaOptions): TExtractFromMappedResult<L, R>
/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */
export function Extract<L extends TTemplateLiteral, R extends TSchema>(type: L, union: R, options?: SchemaOptions): TExtractFromTemplateLiteral<L, R>
/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */
export function Extract<L extends TSchema, R extends TSchema>(type: L, union: R, options?: SchemaOptions): TExtract<L, R>
/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */
export function Extract(L: TSchema, R: TSchema, options?: SchemaOptions): never {
// overloads
if (IsTemplateLiteral(L)) return CreateType(ExtractFromTemplateLiteral(L, R), options) as never
if (IsMappedResult(L)) return CreateType(ExtractFromMappedResult(L, R), options) as never
// prettier-ignore
return CreateType(
IsUnion(L) ? ExtractRest(L.anyOf, R) :
ExtendsCheck(L, R) !== ExtendsResult.False ? L : Never()
, options) as never
}

31
src/type/extract/index.ts Normal file
View File

@@ -0,0 +1,31 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './extract-from-mapped-result'
export * from './extract-from-template-literal'
export * from './extract'

View File

@@ -0,0 +1,71 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { Static } from '../static/index'
import type { Ensure } from '../helpers/index'
import type { TReadonlyOptional } from '../readonly-optional/index'
import type { TReadonly } from '../readonly/index'
import type { TOptional } from '../optional/index'
import { Kind } from '../symbols/index'
// ------------------------------------------------------------------
// StaticFunction
// ------------------------------------------------------------------
type StaticReturnType<U extends TSchema, P extends unknown[]> = Static<U, P>
// prettier-ignore
type StaticParameter<T extends TSchema, P extends unknown[]> =
T extends TReadonlyOptional<T> ? [Readonly<Static<T, P>>?] :
T extends TReadonly<T> ? [Readonly<Static<T, P>>] :
T extends TOptional<T> ? [Static<T, P>?] :
[Static<T, P>]
// prettier-ignore
type StaticParameters<T extends TSchema[], P extends unknown[], Acc extends unknown[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? StaticParameters<R, P, [...Acc, ...StaticParameter<L, P>]>
: Acc
)
// prettier-ignore
type StaticFunction<T extends TSchema[], U extends TSchema, P extends unknown[]> =
Ensure<(...param: StaticParameters<T, P>) => StaticReturnType<U, P>>
// ------------------------------------------------------------------
// TFunction
// ------------------------------------------------------------------
export interface TFunction<T extends TSchema[] = TSchema[], U extends TSchema = TSchema> extends TSchema {
[Kind]: 'Function'
static: StaticFunction<T, U, this['params']>
type: 'Function'
parameters: T
returns: U
}
/** `[JavaScript]` Creates a Function type */
export function Function<T extends TSchema[], U extends TSchema>(parameters: [...T], returns: U, options?: SchemaOptions): TFunction<T, U> {
return CreateType({ [Kind]: 'Function', type: 'Function', parameters, returns }, options) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './function'

31
src/type/guard/index.ts Normal file
View File

@@ -0,0 +1,31 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * as KindGuard from './kind'
export * as TypeGuard from './type'
export * as ValueGuard from './value'

307
src/type/guard/kind.ts Normal file
View File

@@ -0,0 +1,307 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 ValueGuard from './value'
import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index'
import { TransformOptions } from '../transform/index'
import type { TAny } from '../any/index'
import type { TArgument } from '../argument/index'
import type { TArray } from '../array/index'
import type { TAsyncIterator } from '../async-iterator/index'
import type { TBoolean } from '../boolean/index'
import type { TComputed } from '../computed/index'
import type { TBigInt } from '../bigint/index'
import type { TConstructor } from '../constructor/index'
import type { TFunction } from '../function/index'
import type { TImport } from '../module/index'
import type { TInteger } from '../integer/index'
import type { TIntersect } from '../intersect/index'
import type { TIterator } from '../iterator/index'
import type { TLiteral, TLiteralValue } from '../literal/index'
import type { TMappedKey, TMappedResult } from '../mapped/index'
import type { TNever } from '../never/index'
import type { TNot } from '../not/index'
import type { TNull } from '../null/index'
import type { TNumber } from '../number/index'
import type { TObject, TProperties } from '../object/index'
import type { TOptional } from '../optional/index'
import type { TPromise } from '../promise/index'
import type { TReadonly } from '../readonly/index'
import type { TRecord } from '../record/index'
import type { TRef } from '../ref/index'
import type { TRegExp } from '../regexp/index'
import type { TSchema } from '../schema/index'
import type { TString } from '../string/index'
import type { TSymbol } from '../symbol/index'
import type { TTemplateLiteral } from '../template-literal/index'
import type { TTuple } from '../tuple/index'
import type { TUint8Array } from '../uint8array/index'
import type { TUndefined } from '../undefined/index'
import type { TUnknown } from '../unknown/index'
import type { TUnion } from '../union/index'
import type { TUnsafe } from '../unsafe/index'
import type { TVoid } from '../void/index'
import type { TDate } from '../date/index'
import type { TThis } from '../recursive/index'
/** `[Kind-Only]` Returns true if this value has a Readonly symbol */
export function IsReadonly<T extends TSchema>(value: T): value is TReadonly<T> {
return ValueGuard.IsObject(value) && value[ReadonlyKind] === 'Readonly'
}
/** `[Kind-Only]` Returns true if this value has a Optional symbol */
export function IsOptional<T extends TSchema>(value: T): value is TOptional<T> {
return ValueGuard.IsObject(value) && value[OptionalKind] === 'Optional'
}
/** `[Kind-Only]` Returns true if the given value is TAny */
export function IsAny(value: unknown): value is TAny {
return IsKindOf(value, 'Any')
}
/** `[Kind-Only]` Returns true if the given value is TArgument */
export function IsArgument(value: unknown): value is TArgument {
return IsKindOf(value, 'Argument')
}
/** `[Kind-Only]` Returns true if the given value is TArray */
export function IsArray(value: unknown): value is TArray {
return IsKindOf(value, 'Array')
}
/** `[Kind-Only]` Returns true if the given value is TAsyncIterator */
export function IsAsyncIterator(value: unknown): value is TAsyncIterator {
return IsKindOf(value, 'AsyncIterator')
}
/** `[Kind-Only]` Returns true if the given value is TBigInt */
export function IsBigInt(value: unknown): value is TBigInt {
return IsKindOf(value, 'BigInt')
}
/** `[Kind-Only]` Returns true if the given value is TBoolean */
export function IsBoolean(value: unknown): value is TBoolean {
return IsKindOf(value, 'Boolean')
}
/** `[Kind-Only]` Returns true if the given value is TComputed */
export function IsComputed(value: unknown): value is TComputed {
return IsKindOf(value, 'Computed')
}
/** `[Kind-Only]` Returns true if the given value is TConstructor */
export function IsConstructor(value: unknown): value is TConstructor {
return IsKindOf(value, 'Constructor')
}
/** `[Kind-Only]` Returns true if the given value is TDate */
export function IsDate(value: unknown): value is TDate {
return IsKindOf(value, 'Date')
}
/** `[Kind-Only]` Returns true if the given value is TFunction */
export function IsFunction(value: unknown): value is TFunction {
return IsKindOf(value, 'Function')
}
/** `[Kind-Only]` Returns true if the given value is TInteger */
export function IsImport(value: unknown): value is TImport {
return IsKindOf(value, 'Import')
}
/** `[Kind-Only]` Returns true if the given value is TInteger */
export function IsInteger(value: unknown): value is TInteger {
return IsKindOf(value, 'Integer')
}
/** `[Kind-Only]` Returns true if the given schema is TProperties */
export function IsProperties(value: unknown): value is TProperties {
return ValueGuard.IsObject(value)
}
/** `[Kind-Only]` Returns true if the given value is TIntersect */
export function IsIntersect(value: unknown): value is TIntersect {
return IsKindOf(value, 'Intersect')
}
/** `[Kind-Only]` Returns true if the given value is TIterator */
export function IsIterator(value: unknown): value is TIterator {
return IsKindOf(value, 'Iterator')
}
/** `[Kind-Only]` Returns true if the given value is a TKind with the given name. */
export function IsKindOf<T extends string>(value: unknown, kind: T): value is Record<PropertyKey, unknown> & { [Kind]: T } {
return ValueGuard.IsObject(value) && Kind in value && value[Kind] === kind
}
/** `[Kind-Only]` Returns true if the given value is TLiteral<string> */
export function IsLiteralString(value: unknown): value is TLiteral<string> {
return IsLiteral(value) && ValueGuard.IsString(value.const)
}
/** `[Kind-Only]` Returns true if the given value is TLiteral<number> */
export function IsLiteralNumber(value: unknown): value is TLiteral<number> {
return IsLiteral(value) && ValueGuard.IsNumber(value.const)
}
/** `[Kind-Only]` Returns true if the given value is TLiteral<boolean> */
export function IsLiteralBoolean(value: unknown): value is TLiteral<boolean> {
return IsLiteral(value) && ValueGuard.IsBoolean(value.const)
}
/** `[Kind-Only]` Returns true if the given value is TLiteralValue */
export function IsLiteralValue(value: unknown): value is TLiteralValue {
return ValueGuard.IsBoolean(value) || ValueGuard.IsNumber(value) || ValueGuard.IsString(value)
}
/** `[Kind-Only]` Returns true if the given value is TLiteral */
export function IsLiteral(value: unknown): value is TLiteral {
return IsKindOf(value, 'Literal')
}
/** `[Kind-Only]` Returns true if the given value is a TMappedKey */
export function IsMappedKey(value: unknown): value is TMappedKey {
return IsKindOf(value, 'MappedKey')
}
/** `[Kind-Only]` Returns true if the given value is TMappedResult */
export function IsMappedResult(value: unknown): value is TMappedResult {
return IsKindOf(value, 'MappedResult')
}
/** `[Kind-Only]` Returns true if the given value is TNever */
export function IsNever(value: unknown): value is TNever {
return IsKindOf(value, 'Never')
}
/** `[Kind-Only]` Returns true if the given value is TNot */
export function IsNot(value: unknown): value is TNot {
return IsKindOf(value, 'Not')
}
/** `[Kind-Only]` Returns true if the given value is TNull */
export function IsNull(value: unknown): value is TNull {
return IsKindOf(value, 'Null')
}
/** `[Kind-Only]` Returns true if the given value is TNumber */
export function IsNumber(value: unknown): value is TNumber {
return IsKindOf(value, 'Number')
}
/** `[Kind-Only]` Returns true if the given value is TObject */
export function IsObject(value: unknown): value is TObject {
return IsKindOf(value, 'Object')
}
/** `[Kind-Only]` Returns true if the given value is TPromise */
export function IsPromise(value: unknown): value is TPromise {
return IsKindOf(value, 'Promise')
}
/** `[Kind-Only]` Returns true if the given value is TRecord */
export function IsRecord(value: unknown): value is TRecord {
return IsKindOf(value, 'Record')
}
/** `[Kind-Only]` Returns true if this value is TRecursive */
export function IsRecursive(value: unknown): value is { [Hint]: 'Recursive' } {
return ValueGuard.IsObject(value) && Hint in value && value[Hint] === 'Recursive'
}
/** `[Kind-Only]` Returns true if the given value is TRef */
export function IsRef(value: unknown): value is TRef {
return IsKindOf(value, 'Ref')
}
/** `[Kind-Only]` Returns true if the given value is TRegExp */
export function IsRegExp(value: unknown): value is TRegExp {
return IsKindOf(value, 'RegExp')
}
/** `[Kind-Only]` Returns true if the given value is TString */
export function IsString(value: unknown): value is TString {
return IsKindOf(value, 'String')
}
/** `[Kind-Only]` Returns true if the given value is TSymbol */
export function IsSymbol(value: unknown): value is TSymbol {
return IsKindOf(value, 'Symbol')
}
/** `[Kind-Only]` Returns true if the given value is TTemplateLiteral */
export function IsTemplateLiteral(value: unknown): value is TTemplateLiteral {
return IsKindOf(value, 'TemplateLiteral')
}
/** `[Kind-Only]` Returns true if the given value is TThis */
export function IsThis(value: unknown): value is TThis {
return IsKindOf(value, 'This')
}
/** `[Kind-Only]` Returns true of this value is TTransform */
export function IsTransform(value: unknown): value is { [TransformKind]: TransformOptions } {
return ValueGuard.IsObject(value) && TransformKind in value
}
/** `[Kind-Only]` Returns true if the given value is TTuple */
export function IsTuple(value: unknown): value is TTuple {
return IsKindOf(value, 'Tuple')
}
/** `[Kind-Only]` Returns true if the given value is TUndefined */
export function IsUndefined(value: unknown): value is TUndefined {
return IsKindOf(value, 'Undefined')
}
/** `[Kind-Only]` Returns true if the given value is TUnion */
export function IsUnion(value: unknown): value is TUnion {
return IsKindOf(value, 'Union')
}
/** `[Kind-Only]` Returns true if the given value is TUint8Array */
export function IsUint8Array(value: unknown): value is TUint8Array {
return IsKindOf(value, 'Uint8Array')
}
/** `[Kind-Only]` Returns true if the given value is TUnknown */
export function IsUnknown(value: unknown): value is TUnknown {
return IsKindOf(value, 'Unknown')
}
/** `[Kind-Only]` Returns true if the given value is a raw TUnsafe */
export function IsUnsafe(value: unknown): value is TUnsafe<unknown> {
return IsKindOf(value, 'Unsafe')
}
/** `[Kind-Only]` Returns true if the given value is TVoid */
export function IsVoid(value: unknown): value is TVoid {
return IsKindOf(value, 'Void')
}
/** `[Kind-Only]` Returns true if the given value is TKind */
export function IsKind(value: unknown): value is Record<PropertyKey, unknown> & { [Kind]: string } {
return ValueGuard.IsObject(value) && Kind in value && ValueGuard.IsString(value[Kind])
}
/** `[Kind-Only]` Returns true if the given value is TSchema */
export function IsSchema(value: unknown): value is TSchema {
// prettier-ignore
return (
IsAny(value) ||
IsArgument(value) ||
IsArray(value) ||
IsBoolean(value) ||
IsBigInt(value) ||
IsAsyncIterator(value) ||
IsComputed(value) ||
IsConstructor(value) ||
IsDate(value) ||
IsFunction(value) ||
IsInteger(value) ||
IsIntersect(value) ||
IsIterator(value) ||
IsLiteral(value) ||
IsMappedKey(value) ||
IsMappedResult(value) ||
IsNever(value) ||
IsNot(value) ||
IsNull(value) ||
IsNumber(value) ||
IsObject(value) ||
IsPromise(value) ||
IsRecord(value) ||
IsRef(value) ||
IsRegExp(value) ||
IsString(value) ||
IsSymbol(value) ||
IsTemplateLiteral(value) ||
IsThis(value) ||
IsTuple(value) ||
IsUndefined(value) ||
IsUnion(value) ||
IsUint8Array(value) ||
IsUnknown(value) ||
IsUnsafe(value) ||
IsVoid(value) ||
IsKind(value)
)
}

659
src/type/guard/type.ts Normal file
View File

@@ -0,0 +1,659 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 ValueGuard from './value'
import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index'
import { TypeBoxError } from '../error/index'
import { TransformOptions } from '../transform/index'
import type { TAny } from '../any/index'
import type { TArgument } from '../argument/index'
import type { TArray } from '../array/index'
import type { TAsyncIterator } from '../async-iterator/index'
import type { TBoolean } from '../boolean/index'
import type { TComputed } from '../computed/index'
import type { TBigInt } from '../bigint/index'
import type { TConstructor } from '../constructor/index'
import type { TFunction } from '../function/index'
import type { TImport } from '../module/index'
import type { TInteger } from '../integer/index'
import type { TIntersect } from '../intersect/index'
import type { TIterator } from '../iterator/index'
import type { TLiteral, TLiteralValue } from '../literal/index'
import type { TMappedKey, TMappedResult } from '../mapped/index'
import type { TNever } from '../never/index'
import type { TNot } from '../not/index'
import type { TNull } from '../null/index'
import type { TNumber } from '../number/index'
import type { TObject, TAdditionalProperties, TProperties } from '../object/index'
import type { TOptional } from '../optional/index'
import type { TPromise } from '../promise/index'
import type { TReadonly } from '../readonly/index'
import type { TRecord } from '../record/index'
import type { TRef } from '../ref/index'
import type { TRegExp } from '../regexp/index'
import type { TSchema } from '../schema/index'
import type { TString } from '../string/index'
import type { TSymbol } from '../symbol/index'
import type { TTemplateLiteral } from '../template-literal/index'
import type { TTuple } from '../tuple/index'
import type { TUint8Array } from '../uint8array/index'
import type { TUndefined } from '../undefined/index'
import type { TUnion } from '../union/index'
import type { TUnknown } from '../unknown/index'
import type { TUnsafe } from '../unsafe/index'
import type { TVoid } from '../void/index'
import type { TDate } from '../date/index'
import type { TThis } from '../recursive/index'
export class TypeGuardUnknownTypeError extends TypeBoxError {}
const KnownTypes = [
'Argument',
'Any',
'Array',
'AsyncIterator',
'BigInt',
'Boolean',
'Computed',
'Constructor',
'Date',
'Enum',
'Function',
'Integer',
'Intersect',
'Iterator',
'Literal',
'MappedKey',
'MappedResult',
'Not',
'Null',
'Number',
'Object',
'Promise',
'Record',
'Ref',
'RegExp',
'String',
'Symbol',
'TemplateLiteral',
'This',
'Tuple',
'Undefined',
'Union',
'Uint8Array',
'Unknown',
'Void',
]
function IsPattern(value: unknown): value is string {
try {
new RegExp(value as string)
return true
} catch {
return false
}
}
function IsControlCharacterFree(value: unknown): value is string {
if (!ValueGuard.IsString(value)) return false
for (let i = 0; i < value.length; i++) {
const code = value.charCodeAt(i)
if ((code >= 7 && code <= 13) || code === 27 || code === 127) {
return false
}
}
return true
}
function IsAdditionalProperties(value: unknown): value is TAdditionalProperties {
return IsOptionalBoolean(value) || IsSchema(value)
}
function IsOptionalBigInt(value: unknown): value is bigint | undefined {
return ValueGuard.IsUndefined(value) || ValueGuard.IsBigInt(value)
}
function IsOptionalNumber(value: unknown): value is number | undefined {
return ValueGuard.IsUndefined(value) || ValueGuard.IsNumber(value)
}
function IsOptionalBoolean(value: unknown): value is boolean | undefined {
return ValueGuard.IsUndefined(value) || ValueGuard.IsBoolean(value)
}
function IsOptionalString(value: unknown): value is string | undefined {
return ValueGuard.IsUndefined(value) || ValueGuard.IsString(value)
}
function IsOptionalPattern(value: unknown): value is string | undefined {
return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value) && IsPattern(value))
}
function IsOptionalFormat(value: unknown): value is string | undefined {
return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value))
}
function IsOptionalSchema(value: unknown): value is boolean | undefined {
return ValueGuard.IsUndefined(value) || IsSchema(value)
}
// ------------------------------------------------------------------
// Modifiers
// ------------------------------------------------------------------
/** Returns true if this value has a Readonly symbol */
export function IsReadonly<T extends TSchema>(value: T): value is TReadonly<T> {
return ValueGuard.IsObject(value) && value[ReadonlyKind] === 'Readonly'
}
/** Returns true if this value has a Optional symbol */
export function IsOptional<T extends TSchema>(value: T): value is TOptional<T> {
return ValueGuard.IsObject(value) && value[OptionalKind] === 'Optional'
}
// ------------------------------------------------------------------
// Types
// ------------------------------------------------------------------
/** Returns true if the given value is TAny */
export function IsAny(value: unknown): value is TAny {
// prettier-ignore
return (
IsKindOf(value, 'Any') &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is TArgument */
export function IsArgument(value: unknown): value is TArgument {
// prettier-ignore
return (
IsKindOf(value, 'Argument') &&
ValueGuard.IsNumber(value.index)
)
}
/** Returns true if the given value is TArray */
export function IsArray(value: unknown): value is TArray {
return (
IsKindOf(value, 'Array') &&
value.type === 'array' &&
IsOptionalString(value.$id) &&
IsSchema(value.items) &&
IsOptionalNumber(value.minItems) &&
IsOptionalNumber(value.maxItems) &&
IsOptionalBoolean(value.uniqueItems) &&
IsOptionalSchema(value.contains) &&
IsOptionalNumber(value.minContains) &&
IsOptionalNumber(value.maxContains)
)
}
/** Returns true if the given value is TAsyncIterator */
export function IsAsyncIterator(value: unknown): value is TAsyncIterator {
// prettier-ignore
return (
IsKindOf(value, 'AsyncIterator') &&
value.type === 'AsyncIterator' &&
IsOptionalString(value.$id) &&
IsSchema(value.items)
)
}
/** Returns true if the given value is TBigInt */
export function IsBigInt(value: unknown): value is TBigInt {
// prettier-ignore
return (
IsKindOf(value, 'BigInt') &&
value.type === 'bigint' &&
IsOptionalString(value.$id) &&
IsOptionalBigInt(value.exclusiveMaximum) &&
IsOptionalBigInt(value.exclusiveMinimum) &&
IsOptionalBigInt(value.maximum) &&
IsOptionalBigInt(value.minimum) &&
IsOptionalBigInt(value.multipleOf)
)
}
/** Returns true if the given value is TBoolean */
export function IsBoolean(value: unknown): value is TBoolean {
// prettier-ignore
return (
IsKindOf(value, 'Boolean') &&
value.type === 'boolean' &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is TComputed */
export function IsComputed(value: unknown): value is TComputed {
// prettier-ignore
return (
IsKindOf(value, 'Computed') &&
ValueGuard.IsString(value.target) &&
ValueGuard.IsArray(value.parameters) &&
value.parameters.every((schema) => IsSchema(schema))
)
}
/** Returns true if the given value is TConstructor */
export function IsConstructor(value: unknown): value is TConstructor {
// prettier-ignore
return (
IsKindOf(value, 'Constructor') &&
value.type === 'Constructor' &&
IsOptionalString(value.$id) &&
ValueGuard.IsArray(value.parameters) &&
value.parameters.every(schema => IsSchema(schema)) &&
IsSchema(value.returns)
)
}
/** Returns true if the given value is TDate */
export function IsDate(value: unknown): value is TDate {
return (
IsKindOf(value, 'Date') &&
value.type === 'Date' &&
IsOptionalString(value.$id) &&
IsOptionalNumber(value.exclusiveMaximumTimestamp) &&
IsOptionalNumber(value.exclusiveMinimumTimestamp) &&
IsOptionalNumber(value.maximumTimestamp) &&
IsOptionalNumber(value.minimumTimestamp) &&
IsOptionalNumber(value.multipleOfTimestamp)
)
}
/** Returns true if the given value is TFunction */
export function IsFunction(value: unknown): value is TFunction {
// prettier-ignore
return (
IsKindOf(value, 'Function') &&
value.type === 'Function' &&
IsOptionalString(value.$id) &&
ValueGuard.IsArray(value.parameters) &&
value.parameters.every(schema => IsSchema(schema)) &&
IsSchema(value.returns)
)
}
/** Returns true if the given value is TImport */
export function IsImport(value: unknown): value is TImport {
// prettier-ignore
return (
IsKindOf(value, 'Import') &&
ValueGuard.HasPropertyKey(value, '$defs') &&
ValueGuard.IsObject(value.$defs) &&
IsProperties(value.$defs) &&
ValueGuard.HasPropertyKey(value, '$ref') &&
ValueGuard.IsString(value.$ref) &&
value.$ref in value.$defs // required
)
}
/** Returns true if the given value is TInteger */
export function IsInteger(value: unknown): value is TInteger {
return (
IsKindOf(value, 'Integer') &&
value.type === 'integer' &&
IsOptionalString(value.$id) &&
IsOptionalNumber(value.exclusiveMaximum) &&
IsOptionalNumber(value.exclusiveMinimum) &&
IsOptionalNumber(value.maximum) &&
IsOptionalNumber(value.minimum) &&
IsOptionalNumber(value.multipleOf)
)
}
/** Returns true if the given schema is TProperties */
export function IsProperties(value: unknown): value is TProperties {
// prettier-ignore
return (
ValueGuard.IsObject(value) &&
Object.entries(value).every(([key, schema]) => IsControlCharacterFree(key) && IsSchema(schema))
)
}
/** Returns true if the given value is TIntersect */
export function IsIntersect(value: unknown): value is TIntersect {
// prettier-ignore
return (
IsKindOf(value, 'Intersect') &&
(ValueGuard.IsString(value.type) && value.type !== 'object' ? false : true) &&
ValueGuard.IsArray(value.allOf) &&
value.allOf.every(schema => IsSchema(schema) && !IsTransform(schema)) &&
IsOptionalString(value.type) &&
(IsOptionalBoolean(value.unevaluatedProperties) || IsOptionalSchema(value.unevaluatedProperties)) &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is TIterator */
export function IsIterator(value: unknown): value is TIterator {
// prettier-ignore
return (
IsKindOf(value, 'Iterator') &&
value.type === 'Iterator' &&
IsOptionalString(value.$id) &&
IsSchema(value.items)
)
}
/** Returns true if the given value is a TKind with the given name. */
export function IsKindOf<T extends string>(value: unknown, kind: T): value is Record<PropertyKey, unknown> & { [Kind]: T } {
return ValueGuard.IsObject(value) && Kind in value && value[Kind] === kind
}
/** Returns true if the given value is TLiteral<string> */
export function IsLiteralString(value: unknown): value is TLiteral<string> {
return IsLiteral(value) && ValueGuard.IsString(value.const)
}
/** Returns true if the given value is TLiteral<number> */
export function IsLiteralNumber(value: unknown): value is TLiteral<number> {
return IsLiteral(value) && ValueGuard.IsNumber(value.const)
}
/** Returns true if the given value is TLiteral<boolean> */
export function IsLiteralBoolean(value: unknown): value is TLiteral<boolean> {
return IsLiteral(value) && ValueGuard.IsBoolean(value.const)
}
/** Returns true if the given value is TLiteral */
export function IsLiteral(value: unknown): value is TLiteral {
// prettier-ignore
return (
IsKindOf(value, 'Literal') &&
IsOptionalString(value.$id) && IsLiteralValue(value.const)
)
}
/** Returns true if the given value is a TLiteralValue */
export function IsLiteralValue(value: unknown): value is TLiteralValue {
return ValueGuard.IsBoolean(value) || ValueGuard.IsNumber(value) || ValueGuard.IsString(value)
}
/** Returns true if the given value is a TMappedKey */
export function IsMappedKey(value: unknown): value is TMappedKey {
// prettier-ignore
return (
IsKindOf(value, 'MappedKey') &&
ValueGuard.IsArray(value.keys) &&
value.keys.every(key => ValueGuard.IsNumber(key) || ValueGuard.IsString(key))
)
}
/** Returns true if the given value is TMappedResult */
export function IsMappedResult(value: unknown): value is TMappedResult {
// prettier-ignore
return (
IsKindOf(value, 'MappedResult') &&
IsProperties(value.properties)
)
}
/** Returns true if the given value is TNever */
export function IsNever(value: unknown): value is TNever {
// prettier-ignore
return (
IsKindOf(value, 'Never') &&
ValueGuard.IsObject(value.not) &&
Object.getOwnPropertyNames(value.not).length === 0
)
}
/** Returns true if the given value is TNot */
export function IsNot(value: unknown): value is TNot {
// prettier-ignore
return (
IsKindOf(value, 'Not') &&
IsSchema(value.not)
)
}
/** Returns true if the given value is TNull */
export function IsNull(value: unknown): value is TNull {
// prettier-ignore
return (
IsKindOf(value, 'Null') &&
value.type === 'null' &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is TNumber */
export function IsNumber(value: unknown): value is TNumber {
return (
IsKindOf(value, 'Number') &&
value.type === 'number' &&
IsOptionalString(value.$id) &&
IsOptionalNumber(value.exclusiveMaximum) &&
IsOptionalNumber(value.exclusiveMinimum) &&
IsOptionalNumber(value.maximum) &&
IsOptionalNumber(value.minimum) &&
IsOptionalNumber(value.multipleOf)
)
}
/** Returns true if the given value is TObject */
export function IsObject(value: unknown): value is TObject {
// prettier-ignore
return (
IsKindOf(value, 'Object') &&
value.type === 'object' &&
IsOptionalString(value.$id) &&
IsProperties(value.properties) &&
IsAdditionalProperties(value.additionalProperties) &&
IsOptionalNumber(value.minProperties) &&
IsOptionalNumber(value.maxProperties)
)
}
/** Returns true if the given value is TPromise */
export function IsPromise(value: unknown): value is TPromise {
// prettier-ignore
return (
IsKindOf(value, 'Promise') &&
value.type === 'Promise' &&
IsOptionalString(value.$id) &&
IsSchema(value.item)
)
}
/** Returns true if the given value is TRecord */
export function IsRecord(value: unknown): value is TRecord {
// prettier-ignore
return (
IsKindOf(value, 'Record') &&
value.type === 'object' &&
IsOptionalString(value.$id) &&
IsAdditionalProperties(value.additionalProperties) &&
ValueGuard.IsObject(value.patternProperties) &&
((schema: Record<PropertyKey, unknown>) => {
const keys = Object.getOwnPropertyNames(schema.patternProperties)
return (
keys.length === 1 &&
IsPattern(keys[0]) &&
ValueGuard.IsObject(schema.patternProperties) &&
IsSchema(schema.patternProperties[keys[0]])
)
})(value)
)
}
/** Returns true if this value is TRecursive */
export function IsRecursive(value: unknown): value is { [Hint]: 'Recursive' } {
return ValueGuard.IsObject(value) && Hint in value && value[Hint] === 'Recursive'
}
/** Returns true if the given value is TRef */
export function IsRef(value: unknown): value is TRef {
// prettier-ignore
return (
IsKindOf(value, 'Ref') &&
IsOptionalString(value.$id) &&
ValueGuard.IsString(value.$ref)
)
}
/** Returns true if the given value is TRegExp */
export function IsRegExp(value: unknown): value is TRegExp {
// prettier-ignore
return (
IsKindOf(value, 'RegExp') &&
IsOptionalString(value.$id) &&
ValueGuard.IsString(value.source) &&
ValueGuard.IsString(value.flags) &&
IsOptionalNumber(value.maxLength) &&
IsOptionalNumber(value.minLength)
)
}
/** Returns true if the given value is TString */
export function IsString(value: unknown): value is TString {
// prettier-ignore
return (
IsKindOf(value, 'String') &&
value.type === 'string' &&
IsOptionalString(value.$id) &&
IsOptionalNumber(value.minLength) &&
IsOptionalNumber(value.maxLength) &&
IsOptionalPattern(value.pattern) &&
IsOptionalFormat(value.format)
)
}
/** Returns true if the given value is TSymbol */
export function IsSymbol(value: unknown): value is TSymbol {
// prettier-ignore
return (
IsKindOf(value, 'Symbol') &&
value.type === 'symbol' &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is TTemplateLiteral */
export function IsTemplateLiteral(value: unknown): value is TTemplateLiteral {
// prettier-ignore
return (
IsKindOf(value, 'TemplateLiteral') &&
value.type === 'string' &&
ValueGuard.IsString(value.pattern) &&
value.pattern[0] === '^' &&
value.pattern[value.pattern.length - 1] === '$'
)
}
/** Returns true if the given value is TThis */
export function IsThis(value: unknown): value is TThis {
// prettier-ignore
return (
IsKindOf(value, 'This') &&
IsOptionalString(value.$id) &&
ValueGuard.IsString(value.$ref)
)
}
/** Returns true of this value is TTransform */
export function IsTransform(value: unknown): value is { [TransformKind]: TransformOptions } {
return ValueGuard.IsObject(value) && TransformKind in value
}
/** Returns true if the given value is TTuple */
export function IsTuple(value: unknown): value is TTuple {
// prettier-ignore
return (
IsKindOf(value, 'Tuple') &&
value.type === 'array' &&
IsOptionalString(value.$id) &&
ValueGuard.IsNumber(value.minItems) &&
ValueGuard.IsNumber(value.maxItems) &&
value.minItems === value.maxItems &&
(( // empty
ValueGuard.IsUndefined(value.items) &&
ValueGuard.IsUndefined(value.additionalItems) &&
value.minItems === 0
) || (
ValueGuard.IsArray(value.items) &&
value.items.every(schema => IsSchema(schema))
))
)
}
/** Returns true if the given value is TUndefined */
export function IsUndefined(value: unknown): value is TUndefined {
// prettier-ignore
return (
IsKindOf(value, 'Undefined') &&
value.type === 'undefined' &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is TUnion<Literal<string | number>[]> */
export function IsUnionLiteral(value: unknown): value is TUnion<TLiteral[]> {
return IsUnion(value) && value.anyOf.every((schema) => IsLiteralString(schema) || IsLiteralNumber(schema))
}
/** Returns true if the given value is TUnion */
export function IsUnion(value: unknown): value is TUnion {
// prettier-ignore
return (
IsKindOf(value, 'Union') &&
IsOptionalString(value.$id) &&
ValueGuard.IsObject(value) &&
ValueGuard.IsArray(value.anyOf) &&
value.anyOf.every(schema => IsSchema(schema))
)
}
/** Returns true if the given value is TUint8Array */
export function IsUint8Array(value: unknown): value is TUint8Array {
// prettier-ignore
return (
IsKindOf(value, 'Uint8Array') &&
value.type === 'Uint8Array' &&
IsOptionalString(value.$id) &&
IsOptionalNumber(value.minByteLength) &&
IsOptionalNumber(value.maxByteLength)
)
}
/** Returns true if the given value is TUnknown */
export function IsUnknown(value: unknown): value is TUnknown {
// prettier-ignore
return (
IsKindOf(value, 'Unknown') &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is a raw TUnsafe */
export function IsUnsafe(value: unknown): value is TUnsafe<unknown> {
return IsKindOf(value, 'Unsafe')
}
/** Returns true if the given value is TVoid */
export function IsVoid(value: unknown): value is TVoid {
// prettier-ignore
return (
IsKindOf(value, 'Void') &&
value.type === 'void' &&
IsOptionalString(value.$id)
)
}
/** Returns true if the given value is TKind */
export function IsKind(value: unknown): value is Record<PropertyKey, unknown> & { [Kind]: string } {
return ValueGuard.IsObject(value) && Kind in value && ValueGuard.IsString(value[Kind]) && !KnownTypes.includes(value[Kind] as string)
}
/** Returns true if the given value is TSchema */
export function IsSchema(value: unknown): value is TSchema {
// prettier-ignore
return (
ValueGuard.IsObject(value)
) && (
IsAny(value) ||
IsArgument(value) ||
IsArray(value) ||
IsBoolean(value) ||
IsBigInt(value) ||
IsAsyncIterator(value) ||
IsComputed(value) ||
IsConstructor(value) ||
IsDate(value) ||
IsFunction(value) ||
IsInteger(value) ||
IsIntersect(value) ||
IsIterator(value) ||
IsLiteral(value) ||
IsMappedKey(value) ||
IsMappedResult(value) ||
IsNever(value) ||
IsNot(value) ||
IsNull(value) ||
IsNumber(value) ||
IsObject(value) ||
IsPromise(value) ||
IsRecord(value) ||
IsRef(value) ||
IsRegExp(value) ||
IsString(value) ||
IsSymbol(value) ||
IsTemplateLiteral(value) ||
IsThis(value) ||
IsTuple(value) ||
IsUndefined(value) ||
IsUnion(value) ||
IsUint8Array(value) ||
IsUnknown(value) ||
IsUnsafe(value) ||
IsVoid(value) ||
IsKind(value)
)
}

98
src/type/guard/value.ts Normal file
View File

@@ -0,0 +1,98 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
// --------------------------------------------------------------------------
// PropertyKey
// --------------------------------------------------------------------------
/** Returns true if this value has this property key */
export function HasPropertyKey<K extends PropertyKey>(value: Record<any, unknown>, key: K): value is Record<PropertyKey, unknown> & { [_ in K]: unknown } {
return key in value
}
// --------------------------------------------------------------------------
// Object Instances
// --------------------------------------------------------------------------
/** Returns true if this value is an async iterator */
export function IsAsyncIterator(value: unknown): value is AsyncIterableIterator<unknown> {
return IsObject(value) && !IsArray(value) && !IsUint8Array(value) && Symbol.asyncIterator in value
}
/** Returns true if this value is an array */
export function IsArray(value: unknown): value is unknown[] {
return Array.isArray(value)
}
/** Returns true if this value is bigint */
export function IsBigInt(value: unknown): value is bigint {
return typeof value === 'bigint'
}
/** Returns true if this value is a boolean */
export function IsBoolean(value: unknown): value is boolean {
return typeof value === 'boolean'
}
/** Returns true if this value is a Date object */
export function IsDate(value: unknown): value is Date {
return value instanceof globalThis.Date
}
/** Returns true if this value is a function */
export function IsFunction(value: unknown): value is Function {
return typeof value === 'function'
}
/** Returns true if this value is an iterator */
export function IsIterator(value: unknown): value is IterableIterator<unknown> {
return IsObject(value) && !IsArray(value) && !IsUint8Array(value) && Symbol.iterator in value
}
/** Returns true if this value is null */
export function IsNull(value: unknown): value is null {
return value === null
}
/** Returns true if this value is number */
export function IsNumber(value: unknown): value is number {
return typeof value === 'number'
}
/** Returns true if this value is an object */
export function IsObject(value: unknown): value is Record<PropertyKey, unknown> {
return typeof value === 'object' && value !== null
}
/** Returns true if this value is RegExp */
export function IsRegExp(value: unknown): value is RegExp {
return value instanceof globalThis.RegExp
}
/** Returns true if this value is string */
export function IsString(value: unknown): value is string {
return typeof value === 'string'
}
/** Returns true if this value is symbol */
export function IsSymbol(value: unknown): value is symbol {
return typeof value === 'symbol'
}
/** Returns true if this value is a Uint8Array */
export function IsUint8Array(value: unknown): value is Uint8Array {
return value instanceof globalThis.Uint8Array
}
/** Returns true if this value is undefined */
export function IsUndefined(value: unknown): value is undefined {
return value === undefined
}

View File

@@ -0,0 +1,69 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema } from '../schema/index'
import type { TProperties } from '../object/index'
import type { TNever } from '../never/index'
// ------------------------------------------------------------------
// Helper: Common
// ------------------------------------------------------------------
export type TupleToIntersect<T extends any[]> = T extends [infer I] ? I : T extends [infer I, ...infer R] ? I & TupleToIntersect<R> : never
export type TupleToUnion<T extends any[]> = { [K in keyof T]: T[K] }[number]
export type UnionToIntersect<U> = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never
export type UnionLast<U> = UnionToIntersect<U extends unknown ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L : never
export type UnionToTuple<U, Acc extends unknown[] = [], R = UnionLast<U>> = [U] extends [never] ? Acc : UnionToTuple<Exclude<U, R>, [Extract<U, R>, ...Acc]>
export type Trim<T> = T extends `${' '}${infer U}` ? Trim<U> : T extends `${infer U}${' '}` ? Trim<U> : T
export type Assert<T, E> = T extends E ? T : never
export type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
export type Ensure<T> = T extends infer U ? U : never
export type EmptyString = ''
export type ZeroString = '0'
// ------------------------------------------------------------------
// Helper: Increment
// ------------------------------------------------------------------
type IncrementBase = { m: '9'; t: '01'; '0': '1'; '1': '2'; '2': '3'; '3': '4'; '4': '5'; '5': '6'; '6': '7'; '7': '8'; '8': '9'; '9': '0' }
type IncrementTake<T extends keyof IncrementBase> = IncrementBase[T]
type IncrementStep<T extends string> = T extends IncrementBase['m']
? IncrementBase['t']
: T extends `${infer L extends keyof IncrementBase}${infer R}`
? L extends IncrementBase['m']
? `${IncrementTake<L>}${IncrementStep<R>}`
: `${IncrementTake<L>}${R}`
: never
type IncrementReverse<T extends string> = T extends `${infer L}${infer R}` ? `${IncrementReverse<R>}${L}` : T
export type TIncrement<T extends string> = IncrementReverse<IncrementStep<IncrementReverse<T>>>
/** Increments the given string value + 1 */
export function Increment<T extends string>(T: T): TIncrement<T> {
return (parseInt(T) + 1).toString() as never
}
// ------------------------------------------------------------------
// Helper: Type Asserts
// ------------------------------------------------------------------
export type AssertProperties<T> = T extends TProperties ? T : TProperties
export type AssertRest<T, E extends TSchema[] = TSchema[]> = T extends E ? T : []
export type AssertType<T, E extends TSchema = TSchema> = T extends E ? T : TNever

29
src/type/helpers/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './helpers'

99
src/type/index.ts Normal file
View File

@@ -0,0 +1,99 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './any/index'
export * from './argument/index'
export * from './array/index'
export * from './async-iterator/index'
export * from './awaited/index'
export * from './bigint/index'
export * from './boolean/index'
export * from './clone/index'
export * from './composite/index'
export * from './const/index'
export * from './constructor/index'
export * from './constructor-parameters/index'
export * from './date/index'
export * from './discard/index'
export * from './enum/index'
export * from './error/index'
export * from './exclude/index'
export * from './extends/index'
export * from './extract/index'
export * from './function/index'
export * from './guard/index'
export * from './helpers/index'
export * from './indexed/index'
export * from './instance-type/index'
export * from './instantiate/index'
export * from './integer/index'
export * from './intersect/index'
export * from './intrinsic/index'
export * from './iterator/index'
export * from './keyof/index'
export * from './literal/index'
export * from './mapped/index'
export * from './module/index'
export * from './never/index'
export * from './not/index'
export * from './null/index'
export * from './number/index'
export * from './object/index'
export * from './omit/index'
export * from './optional/index'
export * from './parameters/index'
export * from './partial/index'
export * from './patterns/index'
export * from './pick/index'
export * from './promise/index'
export * from './readonly/index'
export * from './readonly-optional/index'
export * from './record/index'
export * from './recursive/index'
export * from './ref/index'
export * from './regexp/index'
export * from './registry/index'
export * from './required/index'
export * from './rest/index'
export * from './return-type/index'
export * from './schema/index'
export * from './sets/index'
export * from './static/index'
export * from './string/index'
export * from './symbol/index'
export * from './symbols/index'
export * from './template-literal/index'
export * from './transform/index'
export * from './tuple/index'
export * from './type/index'
export * from './uint8array/index'
export * from './undefined/index'
export * from './union/index'
export * from './unknown/index'
export * from './unsafe/index'
export * from './void/index'

32
src/type/indexed/index.ts Normal file
View File

@@ -0,0 +1,32 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './indexed-from-mapped-key'
export * from './indexed-from-mapped-result'
export * from './indexed-property-keys'
export * from './indexed'

View File

@@ -0,0 +1,92 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import type { Ensure, Evaluate } from '../helpers/index'
import type { TProperties } from '../object/index'
import { Index, type TIndex } from './indexed'
import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index'
import { Clone } from '../clone/value'
// ------------------------------------------------------------------
// MappedIndexPropertyKey
// ------------------------------------------------------------------
// prettier-ignore
type TMappedIndexPropertyKey<Type extends TSchema, Key extends PropertyKey> = {
[_ in Key]: TIndex<Type, [Key]>
}
// prettier-ignore
function MappedIndexPropertyKey<Type extends TSchema, Key extends PropertyKey>(type: Type, key: Key, options?: SchemaOptions): TMappedIndexPropertyKey<Type, Key> {
return { [key]: Index(type, [key], Clone(options)) } as never
}
// ------------------------------------------------------------------
// MappedIndexPropertyKeys
// ------------------------------------------------------------------
// prettier-ignore
type TMappedIndexPropertyKeys<Type extends TSchema, PropertyKeys extends PropertyKey[], Result extends TProperties = {}> = (
PropertyKeys extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]]
? TMappedIndexPropertyKeys<Type, Right, Result & TMappedIndexPropertyKey<Type, Left>>
: Result
)
// prettier-ignore
function MappedIndexPropertyKeys<
Type extends TSchema,
PropertyKeys extends PropertyKey[]
>(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedIndexPropertyKeys<Type, PropertyKeys> {
return propertyKeys.reduce((result, left) => {
return { ...result, ...MappedIndexPropertyKey(type, left, options) }
}, {} as TProperties) as never
}
// ------------------------------------------------------------------
// MappedIndexProperties
// ------------------------------------------------------------------
// prettier-ignore
type TMappedIndexProperties<Type extends TSchema, MappedKey extends TMappedKey> = Evaluate<
TMappedIndexPropertyKeys<Type, MappedKey['keys']>
>
// prettier-ignore
function MappedIndexProperties<Type extends TSchema, MappedKey extends TMappedKey
>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedIndexProperties<Type, MappedKey> {
return MappedIndexPropertyKeys(type, mappedKey.keys, options) as never
}
// ------------------------------------------------------------------
// TIndexFromMappedKey
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexFromMappedKey<Type extends TSchema, MappedKey extends TMappedKey,
Properties extends TProperties = TMappedIndexProperties<Type, MappedKey>
> = (
Ensure<TMappedResult<Properties>>
)
// prettier-ignore
export function IndexFromMappedKey<Type extends TSchema, MappedKey extends TMappedKey,
Properties extends TProperties = TMappedIndexProperties<Type, MappedKey>
>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedResult<Properties> {
const properties = MappedIndexProperties(type, mappedKey, options)
return MappedResult(properties) as never
}

View File

@@ -0,0 +1,76 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { TSchema, SchemaOptions } from '../schema/index'
import type { TProperties } from '../object/index'
import { MappedResult, type TMappedResult } from '../mapped/index'
import { IndexPropertyKeys, type TIndexPropertyKeys } from './indexed-property-keys'
import { Index, type TIndex } from './index'
// ------------------------------------------------------------------
// FromProperties
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperties<Type extends TSchema, Properties extends TProperties> = (
{ [K2 in keyof Properties]: TIndex<Type, TIndexPropertyKeys<Properties[K2]>> }
)
// prettier-ignore
function FromProperties<Type extends TSchema, Properties extends TProperties>(type: Type, properties: Properties, options?: SchemaOptions): TFromProperties<Type, Properties> {
const result = {} as Record<PropertyKey, TSchema>
for(const K2 of Object.getOwnPropertyNames(properties)) {
result[K2] = Index(type, IndexPropertyKeys(properties[K2]), options)
}
return result as never
}
// ------------------------------------------------------------------
// FromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
type TFromMappedResult<Type extends TSchema, MappedResult extends TMappedResult> = (
TFromProperties<Type, MappedResult['properties']>
)
// prettier-ignore
function FromMappedResult<Type extends TSchema, MappedResult extends TMappedResult>(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TFromMappedResult<Type, MappedResult> {
return FromProperties(type, mappedResult.properties, options) as never
}
// ------------------------------------------------------------------
// TIndexFromMappedResult
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexFromMappedResult<Type extends TSchema, MappedResult extends TMappedResult,
Properties extends TProperties = TFromMappedResult<Type, MappedResult>
> = (
TMappedResult<Properties>
)
// prettier-ignore
export function IndexFromMappedResult<Type extends TSchema, MappedResult extends TMappedResult,
Properties extends TProperties = TFromMappedResult<Type, MappedResult>
>(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TMappedResult<Properties> {
const properties = FromMappedResult(type, mappedResult, options)
return MappedResult(properties) as never
}

View File

@@ -0,0 +1,103 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { TemplateLiteralGenerate, type TTemplateLiteralGenerate, type TTemplateLiteral } from '../template-literal/index'
import type { TLiteral, TLiteralValue } from '../literal/index'
import type { TInteger } from '../integer/index'
import type { TNumber } from '../number/index'
import type { TSchema } from '../schema/index'
import type { TUnion } from '../union/index'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsTemplateLiteral, IsUnion, IsLiteral, IsNumber, IsInteger } from '../guard/kind'
// ------------------------------------------------------------------
// FromTemplateLiteral
// ------------------------------------------------------------------
// prettier-ignore
type TFromTemplateLiteral<TemplateLiteral extends TTemplateLiteral, Keys extends string[] = TTemplateLiteralGenerate<TemplateLiteral>> = (Keys)
// prettier-ignore
function FromTemplateLiteral<TemplateLiteral extends TTemplateLiteral>(templateLiteral: TemplateLiteral): TFromTemplateLiteral<TemplateLiteral> {
const keys = TemplateLiteralGenerate(templateLiteral) as string[]
return keys.map(key => key.toString()) as never
}
// ------------------------------------------------------------------
// FromUnion
// ------------------------------------------------------------------
// prettier-ignore
type TFromUnion<Types extends TSchema[], Result extends string[] = []> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? TFromUnion<Right, [...Result, ...TIndexPropertyKeys<Left>]>
: Result
)
// prettier-ignore
function FromUnion<Types extends TSchema[]>(types: Types): TFromUnion<Types> {
const result = [] as string[]
for(const type of types) result.push(...IndexPropertyKeys(type))
return result as never
}
// ------------------------------------------------------------------
// FromLiteral
// ------------------------------------------------------------------
// prettier-ignore
type TFromLiteral<LiteralValue extends TLiteralValue> = (
LiteralValue extends PropertyKey
? [`${LiteralValue}`]
: []
)
// prettier-ignore
function FromLiteral<LiteralValue extends TLiteralValue>(literalValue: LiteralValue): TFromLiteral<LiteralValue> {
return (
[(literalValue as string).toString()] // TS 5.4 observes TLiteralValue as not having a toString()
) as never
}
// ------------------------------------------------------------------
// IndexPropertyKeys
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexPropertyKeys<Type extends TSchema> = (
Type extends TTemplateLiteral ? TFromTemplateLiteral<Type> :
Type extends TUnion<infer Types extends TSchema[]> ? TFromUnion<Types> :
Type extends TLiteral<infer Value extends TLiteralValue> ? TFromLiteral<Value> :
Type extends TNumber ? ['[number]'] :
Type extends TInteger ? ['[number]'] :
[]
)
/** Returns a tuple of PropertyKeys derived from the given TSchema */
// prettier-ignore
export function IndexPropertyKeys<Type extends TSchema>(type: Type): TIndexPropertyKeys<Type> {
return [...new Set<string>((
IsTemplateLiteral(type) ? FromTemplateLiteral(type) :
IsUnion(type) ? FromUnion(type.anyOf) :
IsLiteral(type) ? FromLiteral(type.const) :
IsNumber(type) ? ['[number]'] :
IsInteger(type) ? ['[number]'] :
[]
))] as never
}

300
src/type/indexed/indexed.ts Normal file
View File

@@ -0,0 +1,300 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import { TypeBoxError } from '../error/index'
import { type TSchema, SchemaOptions } from '../schema/index'
import { type Assert } from '../helpers/index'
import { type TComputed, Computed } from '../computed/index'
import { type TNever, Never } from '../never/index'
import { type TArray } from '../array/index'
import { type TIntersect } from '../intersect/index'
import { type TMappedResult, type TMappedKey } from '../mapped/index'
import { type TObject, type TProperties } from '../object/index'
import { type TUnion } from '../union/index'
import { type TRecursive } from '../recursive/index'
import { type TRef } from '../ref/index'
import { type TTuple } from '../tuple/index'
import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index'
import { UnionEvaluated, type TUnionEvaluated } from '../union/index'
import { IndexPropertyKeys, type TIndexPropertyKeys } from './indexed-property-keys'
import { IndexFromMappedKey, type TIndexFromMappedKey } from './indexed-from-mapped-key'
import { IndexFromMappedResult, type TIndexFromMappedResult } from './indexed-from-mapped-result'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion, IsRef } from '../guard/kind'
// ------------------------------------------------------------------
// FromRest
// ------------------------------------------------------------------
// prettier-ignore
type TFromRest<Types extends TSchema[], Key extends PropertyKey, Result extends TSchema[] = []> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? TFromRest<Right, Key, [...Result, Assert<TIndexFromPropertyKey<Left, Key>, TSchema>]>
: Result
)
// prettier-ignore
function FromRest<Types extends TSchema[], Key extends PropertyKey>(types: [...Types], key: Key): TFromRest<Types, Key> {
return types.map(type => IndexFromPropertyKey(type, key)) as never
}
// ------------------------------------------------------------------
// FromIntersectRest
// ------------------------------------------------------------------
// prettier-ignore
type TFromIntersectRest<Types extends TSchema[], Result extends TSchema[] = []> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? Left extends TNever
? TFromIntersectRest<Right, [...Result]>
: TFromIntersectRest<Right, [...Result, Left]>
: Result
)
// prettier-ignore
function FromIntersectRest<Types extends TSchema[]>(types: [...Types]): TFromIntersectRest<Types> {
return types.filter(type => !IsNever(type)) as never
}
// prettier-ignore
type TFromIntersect<Types extends TSchema[], Key extends PropertyKey> = (
TIntersectEvaluated<TFromIntersectRest<TFromRest<Types, Key>>>
)
// prettier-ignore
function FromIntersect<Types extends TSchema[], Key extends PropertyKey>(types: [...Types], key: Key): TFromIntersect<Types, Key> {
return (
IntersectEvaluated(FromIntersectRest(FromRest(types as TSchema[], key)))
) as never
}
// ------------------------------------------------------------------
// FromUnionRest
//
// The following accept a tuple of indexed key results. When evaluating
// these results, we check if any result evaluated to TNever. For key
// indexed unions, a TNever result indicates that the key was not
// present on the variant. In these cases, we must evaluate the indexed
// union to TNever (as given by a [] result). This logic aligns to the
// following behaviour.
//
// Non-Overlapping Union
//
// type A = { a: string }
// type B = { b: string }
// type C = (A | B) & { a: number } // C is { a: number }
//
// Overlapping Union
//
// type A = { a: string }
// type B = { a: string }
// type C = (A | B) & { a: number } // C is { a: never }
//
// ------------------------------------------------------------------
// prettier-ignore
type TFromUnionRest<Types extends TSchema[], Result extends TSchema[] = []> =
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? Left extends TNever
? []
: TFromUnionRest<Right, [Left, ...Result]>
: Result
// prettier-ignore
function FromUnionRest<Types extends TSchema[]>(types: [...Types]): TFromUnionRest<Types> {
return (
types.some(L => IsNever(L))
? []
: types
) as never
}
// ------------------------------------------------------------------
// FromUnion
// ------------------------------------------------------------------
// prettier-ignore
type TFromUnion<Types extends TSchema[], Key extends PropertyKey> = (
TUnionEvaluated<TFromUnionRest<TFromRest<Types, Key>>>
)
// prettier-ignore
function FromUnion<Types extends TSchema[], Key extends PropertyKey>(types: [...Types], key: Key): TFromUnion<Types, Key> {
return (
UnionEvaluated(FromUnionRest(FromRest(types as TSchema[], key)))
) as never
}
// ------------------------------------------------------------------
// FromTuple
// ------------------------------------------------------------------
// prettier-ignore
type TFromTuple<Types extends TSchema[], Key extends PropertyKey> = (
Key extends keyof Types ? Types[Key] :
Key extends '[number]' ? TUnionEvaluated<Types> :
TNever
)
// prettier-ignore
function FromTuple<Types extends TSchema[], Key extends PropertyKey>(types: [...Types], key: Key): TFromTuple<Types, Key> {
return (
key in types ? types[key as number] :
key === '[number]' ? UnionEvaluated(types) :
Never()
) as never
}
// ------------------------------------------------------------------
// FromArray
// ------------------------------------------------------------------
// prettier-ignore
type TFromArray<Type extends TSchema, Key extends PropertyKey> = (
Key extends '[number]'
? Type
: TNever
)
// prettier-ignore
function FromArray<Type extends TSchema, Key extends PropertyKey>(type: Type, key: Key): TFromArray<Type, Key> {
return (
key === '[number]'
? type
: Never()
) as never
}
// ------------------------------------------------------------------
// FromProperty
// ------------------------------------------------------------------
type AssertPropertyKey<T> = Assert<T, string | number>
// prettier-ignore
type TFromProperty<Properties extends TProperties, Key extends PropertyKey> = (
// evaluate for string keys
Key extends keyof Properties
? Properties[Key]
// evaluate for numeric keys
: `${AssertPropertyKey<Key>}` extends `${AssertPropertyKey<keyof Properties>}`
? Properties[AssertPropertyKey<Key>]
: TNever
)
// prettier-ignore
function FromProperty<Properties extends TProperties, Key extends PropertyKey>(properties: Properties, propertyKey: Key): TFromProperty<Properties, Key> {
return (propertyKey in properties ? properties[propertyKey as string] : Never()) as never
}
// ------------------------------------------------------------------
// FromKey
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexFromPropertyKey<Type extends TSchema, Key extends PropertyKey> = (
Type extends TRecursive<infer Type extends TSchema> ? TIndexFromPropertyKey<Type, Key> :
Type extends TIntersect<infer Types extends TSchema[]> ? TFromIntersect<Types, Key> :
Type extends TUnion<infer Types extends TSchema[]> ? TFromUnion<Types, Key> :
Type extends TTuple<infer Types extends TSchema[]> ? TFromTuple<Types, Key> :
Type extends TArray<infer Type extends TSchema> ? TFromArray<Type, Key> :
Type extends TObject<infer Properties extends TProperties> ? TFromProperty<Properties, Key> :
TNever
)
// prettier-ignore
export function IndexFromPropertyKey<Type extends TSchema, Key extends PropertyKey>(type: Type, propertyKey: Key): TIndexFromPropertyKey<Type, Key> {
return (
IsIntersect(type) ? FromIntersect(type.allOf, propertyKey) :
IsUnion(type) ? FromUnion(type.anyOf, propertyKey) :
IsTuple(type) ? FromTuple(type.items ?? [], propertyKey) :
IsArray(type) ? FromArray(type.items, propertyKey) :
IsObject(type) ? FromProperty(type.properties, propertyKey) :
Never()
) as never
}
// ------------------------------------------------------------------
// FromKeys
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexFromPropertyKeys<Type extends TSchema, PropertyKeys extends PropertyKey[], Result extends TSchema[] = []> = (
PropertyKeys extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]]
? TIndexFromPropertyKeys<Type, Right, [...Result, Assert<TIndexFromPropertyKey<Type, Left>, TSchema>]>
: Result
)
// prettier-ignore
export function IndexFromPropertyKeys<Type extends TSchema, PropertyKeys extends PropertyKey[]>(type: Type, propertyKeys: [...PropertyKeys]): TIndexFromPropertyKeys<Type, PropertyKeys> {
return propertyKeys.map(propertyKey => IndexFromPropertyKey(type, propertyKey)) as never
}
// ------------------------------------------------------------------
// FromSchema
// ------------------------------------------------------------------
// prettier-ignore
type FromSchema<Type extends TSchema, PropertyKeys extends PropertyKey[]> = (
TUnionEvaluated<TIndexFromPropertyKeys<Type, PropertyKeys>>
)
// prettier-ignore
function FromSchema<Type extends TSchema, PropertyKeys extends PropertyKey[]>(type: Type, propertyKeys: [...PropertyKeys]): FromSchema<Type, PropertyKeys> {
return (
UnionEvaluated(IndexFromPropertyKeys(type, propertyKeys as PropertyKey[]))
) as never
}
// ------------------------------------------------------------------
// FromSchema
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexFromComputed<Type extends TSchema, Key extends TSchema> = (
TComputed<'Index', [Type, Key]>
)
// prettier-ignore
export function IndexFromComputed<Type extends TSchema, Key extends TSchema>(type: Type, key: Key): TIndexFromComputed<Type, Key> {
return Computed('Index', [type, key])
}
// ------------------------------------------------------------------
// TIndex
// ------------------------------------------------------------------
// prettier-ignore
export type TIndex<Type extends TSchema, PropertyKeys extends PropertyKey[]> = (
FromSchema<Type, PropertyKeys>
)
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TRef, Key extends TSchema>(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed<Type, Key>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TSchema, Key extends TRef>(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed<Type, Key>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TRef, Key extends TRef>(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed<Type, Key>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TSchema, MappedResult extends TMappedResult>(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TIndexFromMappedResult<Type, MappedResult>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TSchema, MappedResult extends TMappedResult>(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TIndexFromMappedResult<Type, MappedResult>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TSchema, MappedKey extends TMappedKey>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TIndexFromMappedKey<Type, MappedKey>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TSchema, Key extends TSchema, PropertyKeys extends PropertyKey[] = TIndexPropertyKeys<Key>>(T: Type, K: Key, options?: SchemaOptions): TIndex<Type, PropertyKeys>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index<Type extends TSchema, PropertyKeys extends PropertyKey[]>(type: Type, propertyKeys: readonly [...PropertyKeys], options?: SchemaOptions): TIndex<Type, PropertyKeys>
/** `[Json]` Returns an Indexed property type for the given keys */
export function Index(type: TSchema, key: any, options?: SchemaOptions): any {
// computed-type
if (IsRef(type) || IsRef(key)) {
const error = `Index types using Ref parameters require both Type and Key to be of TSchema`
if (!IsSchema(type) || !IsSchema(key)) throw new TypeBoxError(error)
return Computed('Index', [type, key])
}
// mapped-types
if (IsMappedResult(key)) return IndexFromMappedResult(type, key, options)
if (IsMappedKey(key)) return IndexFromMappedKey(type, key, options)
// prettier-ignore
return CreateType(
IsSchema(key)
? FromSchema(type, IndexPropertyKeys(key))
: FromSchema(type, key as string[])
, options) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './instance-type'

View File

@@ -0,0 +1,45 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import { type TSchema, SchemaOptions } from '../schema/index'
import { type TConstructor } from '../constructor/index'
import { type TNever, Never } from '../never/index'
import * as KindGuard from '../guard/kind'
// prettier-ignore
export type TInstanceType<Type extends TSchema,
Result extends TSchema = Type extends TConstructor<infer _Parameters extends TSchema[], infer InstanceType extends TSchema>
? InstanceType
: TNever
> = Result
/** `[JavaScript]` Extracts the InstanceType from the given Constructor type */
export function InstanceType<Type extends TSchema>(schema: Type, options?: SchemaOptions): TInstanceType<Type> {
return (KindGuard.IsConstructor(schema) ? CreateType(schema.returns, options) : Never(options)) as never
}

View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './instantiate'

View File

@@ -0,0 +1,307 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CloneType } from '../clone/type'
import { type TSchema } from '../schema/index'
import { type TArgument } from '../argument/index'
import { type TUnknown, Unknown } from '../unknown/index'
import { type TReadonlyOptional, ReadonlyOptional } from '../readonly-optional/index'
import { type TReadonly, Readonly } from '../readonly/index'
import { type TOptional, Optional } from '../optional/index'
import { type TConstructor } from '../constructor/index'
import { type TFunction } from '../function/index'
import { type TIntersect } from '../intersect/index'
import { type TUnion } from '../union/index'
import { type TTuple } from '../tuple/index'
import { type TArray } from '../array/index'
import { type TAsyncIterator } from '../async-iterator/index'
import { type TIterator } from '../iterator/index'
import { type TPromise } from '../promise/index'
import { type TObject, type TProperties, Object } from '../object/index'
import { type TRecordOrObject, type TRecord, Record, RecordKey, RecordValue } from '../record/index'
import * as ValueGuard from '../guard/value'
import * as KindGuard from '../guard/kind'
// ------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------
// prettier-ignore
type TFromConstructor<Args extends TSchema[], Parameters extends TSchema[], InstanceType extends TSchema,
Result extends TConstructor = TConstructor<TFromTypes<Args, Parameters>, TFromType<Args, InstanceType>>
> = Result
// prettier-ignore
function FromConstructor(args: TSchema[], type: TConstructor): TConstructor {
type.parameters = FromTypes(args, type.parameters)
type.returns = FromType(args, type.returns)
return type
}
// ------------------------------------------------------------------
// Function
// ------------------------------------------------------------------
// prettier-ignore
type TFromFunction<Args extends TSchema[], Parameters extends TSchema[], ReturnType extends TSchema,
Result extends TFunction = TFunction<TFromTypes<Args, Parameters>, TFromType<Args, ReturnType>>
> = Result
// prettier-ignore
function FromFunction(args: TSchema[], type: TFunction): TFunction {
type.parameters = FromTypes(args, type.parameters)
type.returns = FromType(args, type.returns)
return type
}
// ------------------------------------------------------------------
// Intersect
// ------------------------------------------------------------------
// prettier-ignore
type TFromIntersect<Args extends TSchema[], Types extends TSchema[],
Result extends TIntersect = TIntersect<TFromTypes<Args, Types>>
> = Result
// prettier-ignore
function FromIntersect(args: TSchema[], type: TIntersect): TIntersect {
type.allOf = FromTypes(args, type.allOf)
return type
}
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
// prettier-ignore
type TFromUnion<Args extends TSchema[], Types extends TSchema[],
Result extends TUnion = TUnion<TFromTypes<Args, Types>>
> = Result
// prettier-ignore
function FromUnion(args: TSchema[], type: TUnion): TUnion {
type.anyOf = FromTypes(args, type.anyOf)
return type
}
// ------------------------------------------------------------------
// Tuple
// ------------------------------------------------------------------
// prettier-ignore
type TFromTuple<Args extends TSchema[], Types extends TSchema[],
Result extends TTuple = TTuple<TFromTypes<Args, Types>>
> = Result
// prettier-ignore
function FromTuple(args: TSchema[], type: TTuple): TTuple {
if(ValueGuard.IsUndefined(type.items)) return type
type.items = FromTypes(args, type.items!)
return type
}
// ------------------------------------------------------------------
// Array
// ------------------------------------------------------------------
// prettier-ignore
type TFromArray<Args extends TSchema[], Type extends TSchema,
Result extends TArray = TArray<TFromType<Args, Type>>
> = Result
// prettier-ignore
function FromArray(args: TSchema[], type: TArray): TArray {
type.items = FromType(args, type.items)
return type
}
// ------------------------------------------------------------------
// AsyncIterator
// ------------------------------------------------------------------
// prettier-ignore
type TFromAsyncIterator<Args extends TSchema[], Type extends TSchema,
Result extends TAsyncIterator = TAsyncIterator<TFromType<Args, Type>>
> = Result
// prettier-ignore
function FromAsyncIterator(args: TSchema[], type: TAsyncIterator): TAsyncIterator {
type.items = FromType(args, type.items)
return type
}
// ------------------------------------------------------------------
// Iterator
// ------------------------------------------------------------------
// prettier-ignore
type TFromIterator<Args extends TSchema[], Type extends TSchema,
Result extends TIterator = TIterator<TFromType<Args, Type>>
> = Result
// prettier-ignore
function FromIterator(args: TSchema[], type: TIterator): TIterator {
type.items = FromType(args, type.items)
return type
}
// ------------------------------------------------------------------
// Promise
// ------------------------------------------------------------------
// prettier-ignore
type TFromPromise<Args extends TSchema[], Type extends TSchema,
Result extends TPromise = TPromise<TFromType<Args, Type>>
> = Result
// prettier-ignore
function FromPromise(args: TSchema[], type: TPromise): TPromise {
type.item = FromType(args, type.item)
return type
}
// ------------------------------------------------------------------
// Object
// ------------------------------------------------------------------
// prettier-ignore
type TFromObject<Args extends TSchema[], Properties extends TProperties,
MappedProperties extends TProperties = TFromProperties<Args, Properties>,
Result extends TSchema = TObject<MappedProperties>
> = Result
// prettier-ignore
function FromObject(args: TSchema[], type: TObject): TObject {
const mappedProperties = FromProperties(args, type.properties)
return { ...type, ...Object(mappedProperties) } // retain options
}
// ------------------------------------------------------------------
// Object
// ------------------------------------------------------------------
// prettier-ignore
type TFromRecord<Args extends TSchema[], Key extends TSchema, Value extends TSchema,
MappedKey extends TSchema = TFromType<Args, Key>,
MappedValue extends TSchema = TFromType<Args, Value>,
Result extends TSchema = TRecordOrObject<MappedKey, MappedValue>
> = Result
// prettier-ignore
function FromRecord(args: TSchema[], type: TRecord): TRecord {
const mappedKey = FromType(args, RecordKey(type))
const mappedValue = FromType(args, RecordValue(type))
const result = Record(mappedKey, mappedValue)
return { ...type, ... result } as never // retain options
}
// ------------------------------------------------------------------
// Argument
// ------------------------------------------------------------------
// prettier-ignore
type TFromArgument<Args extends TSchema[], Index extends number,
Result extends TSchema = Index extends keyof Args[Index] ? Args[Index] : TUnknown
> = Result
// prettier-ignore
function FromArgument(args: TSchema[], argument: TArgument): TSchema {
return argument.index in args ? args[argument.index] : Unknown()
}
// ------------------------------------------------------------------
// Property
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperty<Args extends TSchema[], Type extends TSchema,
IsReadonly extends boolean = Type extends TReadonly<Type> ? true : false,
IsOptional extends boolean = Type extends TOptional<Type> ? true : false,
Mapped extends TSchema = TFromType<Args, Type>,
Result extends TSchema = (
[IsReadonly, IsOptional] extends [true, true] ? TReadonlyOptional<Mapped> :
[IsReadonly, IsOptional] extends [true, false] ? TReadonly<Mapped> :
[IsReadonly, IsOptional] extends [false, true] ? TOptional<Mapped> :
Mapped
)
> = Result
// prettier-ignore
function FromProperty<Args extends TSchema[], Type extends TSchema>(args: [...Args], type: Type): TFromProperty<Args, Type> {
const isReadonly = KindGuard.IsReadonly(type)
const isOptional = KindGuard.IsOptional(type)
const mapped = FromType(args, type)
return (
isReadonly && isOptional ? ReadonlyOptional(mapped) :
isReadonly && !isOptional ? Readonly(mapped) :
!isReadonly && isOptional ? Optional(mapped) :
mapped
) as never
}
// ------------------------------------------------------------------
// Properties
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperties<Args extends TSchema[], Properties extends TProperties,
Result extends TProperties = {
[Key in keyof Properties]: TFromProperty<Args, Properties[Key]>
}
> = Result
// prettier-ignore
function FromProperties<Args extends TSchema[], Properties extends TProperties>(args: TSchema[], properties: TProperties): TFromProperties<Args, Properties> {
return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => {
return { ...result, [key]: FromProperty(args, properties[key]) }
}, {}) as never
}
// ------------------------------------------------------------------
// Types
// ------------------------------------------------------------------
// prettier-ignore
export type TFromTypes<Args extends TSchema[], Types extends TSchema[], Result extends TSchema[] = []> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? TFromTypes<Args, Right, [...Result, TFromType<Args, Left>]>
: Result
)
// prettier-ignore
export function FromTypes<Args extends TSchema[], Types extends TSchema[]>(args: [...Args], types: [...Types]): TFromTypes<Args, Types> {
return types.map(type => FromType(args, type)) as never
}
// ------------------------------------------------------------------
// Type
// ------------------------------------------------------------------
// prettier-ignore
export type TFromType<Args extends TSchema[], Type extends TSchema> = (
Type extends TConstructor<infer Parameters extends TSchema[], infer InstanceType extends TSchema> ? TFromConstructor<Args, Parameters, InstanceType> :
Type extends TFunction<infer Parameters extends TSchema[], infer ReturnType extends TSchema> ? TFromFunction<Args, Parameters, ReturnType> :
Type extends TIntersect<infer Types extends TSchema[]> ? TFromIntersect<Args, Types> :
Type extends TUnion<infer Types extends TSchema[]> ? TFromUnion<Args, Types> :
Type extends TTuple<infer Types extends TSchema[]> ? TFromTuple<Args, Types> :
Type extends TArray<infer Type extends TSchema> ? TFromArray<Args, Type>:
Type extends TAsyncIterator<infer Type extends TSchema> ? TFromAsyncIterator<Args, Type> :
Type extends TIterator<infer Type extends TSchema> ? TFromIterator<Args, Type> :
Type extends TPromise<infer Type extends TSchema> ? TFromPromise<Args, Type> :
Type extends TObject<infer Properties extends TProperties> ? TFromObject<Args, Properties> :
Type extends TRecord<infer Key extends TSchema, infer Value extends TSchema> ? TFromRecord<Args, Key, Value> :
Type extends TArgument<infer Index extends number> ? TFromArgument<Args, Index> :
Type
)
// prettier-ignore
function FromType<Args extends TSchema[], Type extends TSchema>(args: [...Args], type: TSchema): TFromType<Args, Type> {
return (
KindGuard.IsConstructor(type) ? FromConstructor(args, type) :
KindGuard.IsFunction(type) ? FromFunction(args, type) :
KindGuard.IsIntersect(type) ? FromIntersect(args, type) :
KindGuard.IsUnion(type) ? FromUnion(args, type) :
KindGuard.IsTuple(type) ? FromTuple(args, type) :
KindGuard.IsArray(type) ? FromArray(args, type) :
KindGuard.IsAsyncIterator(type) ? FromAsyncIterator(args, type) :
KindGuard.IsIterator(type) ? FromIterator(args, type) :
KindGuard.IsPromise(type) ? FromPromise(args, type) :
KindGuard.IsObject(type) ? FromObject(args, type):
KindGuard.IsRecord(type) ? FromRecord(args, type) :
KindGuard.IsArgument(type) ? FromArgument(args, type) :
type
) as never
}
// ------------------------------------------------------------------
// Instantiate
// ------------------------------------------------------------------
/** `[JavaScript]` Instantiates a type with the given parameters */
// prettier-ignore
export type TInstantiate<Type extends TSchema, Args extends TSchema[],
Result extends TSchema = TFromType<Args, Type>
> = Result
/** `[JavaScript]` Instantiates a type with the given parameters */
// prettier-ignore
export function Instantiate<Type extends TSchema, Args extends TSchema[]>(type: Type, args: [...Args]): TInstantiate<Type, Args> {
return FromType(args, CloneType(type))
}

29
src/type/integer/index.ts Normal file
View File

@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './integer'

View File

@@ -0,0 +1,48 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'
export interface IntegerOptions extends SchemaOptions {
exclusiveMaximum?: number
exclusiveMinimum?: number
maximum?: number
minimum?: number
multipleOf?: number
}
export interface TInteger extends TSchema, IntegerOptions {
[Kind]: 'Integer'
static: number
type: 'integer'
}
/** `[Json]` Creates an Integer type */
export function Integer(options?: IntegerOptions): TInteger {
return CreateType({ [Kind]: 'Integer', type: 'integer' }, options) as never
}

View File

@@ -0,0 +1,31 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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.
---------------------------------------------------------------------------*/
export * from './intersect-evaluated'
export * from './intersect-type'
export * from './intersect'

View File

@@ -0,0 +1,52 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 { CreateType } from '../create/type'
import type { TSchema } from '../schema/index'
import { Kind } from '../symbols/index'
import type { TIntersect, IntersectOptions } from './intersect-type'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsObject, IsSchema } from '../guard/kind'
// ------------------------------------------------------------------
// IntersectCreate
// ------------------------------------------------------------------
// prettier-ignore
export function IntersectCreate<T extends TSchema[]>(T: [...T], options: IntersectOptions = {}): TIntersect<T> {
const allObjects = T.every((schema) => IsObject(schema))
const clonedUnevaluatedProperties = IsSchema(options.unevaluatedProperties)
? { unevaluatedProperties: options.unevaluatedProperties }
: {}
return CreateType(
(options.unevaluatedProperties === false || IsSchema(options.unevaluatedProperties) || allObjects
? { ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: T }
: { ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: T })
, options) as never
}

View File

@@ -0,0 +1,122 @@
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-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 type { SchemaOptions, TSchema } from '../schema/index'
import { OptionalKind } from '../symbols/index'
import { CreateType } from '../create/type'
import { Discard } from '../discard/index'
import { Never, type TNever } from '../never/index'
import { Optional, type TOptional } from '../optional/index'
import type { TReadonly } from '../readonly/index'
import { TIntersect, IntersectOptions } from './intersect-type'
import { IntersectCreate } from './intersect-create'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsOptional, IsTransform } from '../guard/kind'
// ------------------------------------------------------------------
// IsIntersectOptional
// ------------------------------------------------------------------
// prettier-ignore
type TIsIntersectOptional<Types extends TSchema[]> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? Left extends TOptional<TSchema>
? TIsIntersectOptional<Right>
: false
: true
)
// prettier-ignore
function IsIntersectOptional<Types extends TSchema[]>(types: [...Types]): TIsIntersectOptional<Types> {
return types.every(left => IsOptional(left)) as never
}
// ------------------------------------------------------------------
// RemoveOptionalFromType
// ------------------------------------------------------------------
// prettier-ignore
type TRemoveOptionalFromType<Type extends TSchema> = (
Type extends TReadonly<infer Type extends TSchema> ? TReadonly<TRemoveOptionalFromType<Type>> :
Type extends TOptional<infer Type extends TSchema> ? TRemoveOptionalFromType<Type> :
Type
)
// prettier-ignore
function RemoveOptionalFromType<Type extends TSchema>(type: Type): TRemoveOptionalFromType<Type> {
return (
Discard(type, [OptionalKind])
) as never
}
// ------------------------------------------------------------------
// RemoveOptionalFromRest
// ------------------------------------------------------------------
// prettier-ignore
type TRemoveOptionalFromRest<Types extends TSchema[], Result extends TSchema[] = []> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? Left extends TOptional<infer Type extends TSchema>
? TRemoveOptionalFromRest<Right, [...Result, TRemoveOptionalFromType<Type>]>
: TRemoveOptionalFromRest<Right, [...Result, Left]>
: Result
)
// prettier-ignore
function RemoveOptionalFromRest<Types extends TSchema[]>(types: [...Types]): TRemoveOptionalFromRest<Types> {
return types.map(left => IsOptional(left) ? RemoveOptionalFromType(left) : left) as never
}
// ------------------------------------------------------------------
// ResolveIntersect
// ------------------------------------------------------------------
// prettier-ignore
type TResolveIntersect<Types extends TSchema[]> = (
TIsIntersectOptional<Types> extends true
? TOptional<TIntersect<TRemoveOptionalFromRest<Types>>>
: TIntersect<TRemoveOptionalFromRest<Types>>
)
// prettier-ignore
function ResolveIntersect<Types extends TSchema[]>(types: [...Types], options: SchemaOptions): TResolveIntersect<Types> {
return (
IsIntersectOptional(types)
? Optional(IntersectCreate(RemoveOptionalFromRest(types) as TSchema[], options))
: IntersectCreate(RemoveOptionalFromRest(types) as TSchema[], options)
) as never
}
// ------------------------------------------------------------------
// IntersectEvaluated
// ------------------------------------------------------------------
// prettier-ignore
export type TIntersectEvaluated<Types extends TSchema[]> = (
Types extends [TSchema] ? Types[0] :
Types extends [] ? TNever :
TResolveIntersect<Types>
)
/** `[Json]` Creates an evaluated Intersect type */
export function IntersectEvaluated<Types extends TSchema[], Result extends TSchema = TIntersectEvaluated<Types>>(types: [...Types], options: IntersectOptions = {}): Result {
if (types.length === 1) return CreateType(types[0], options) as never
if (types.length === 0) return Never(options) as never
if (types.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types')
return ResolveIntersect(types, options) as never
}

Some files were not shown because too many files have changed in this diff Show More