The MetaTyper project provides a powerful approach to using runtime types in TypeScript and JavaScript code
npm install metatyper
code faster, smarter, better
---
      
The MetaTyper project provides a powerful approach to using runtime types in TypeScript and JavaScript code.
It is based on the principle of using classical objects or classes as a data schema for further validation and serialization.
The goal of the project is to make runtime types as developer-friendly as possible.
More Facts:
- Works in Node.JS and all modern browsers.
- Automatically infers TypeScript types.
- Works with native JavaScript.
- It's tiny.
- Zero dependencies.
- Rich error details.
- Rich extensibility support.
``bash`
npm install metatyper
or
`bash`
yarn add metatyper
or
`html
`
> In this case, you need to use this library through the MetaTyper global variable: MetaTyper.Meta({ / ... / })
First, you can create a Meta object.
`typescript
import { Meta, NUMBER } from 'metatyper'
// Simple product model with runtime validation
const product = Meta({
id: 1,
name: 'Example product',
price: NUMBER({ min: 0, default: 0 })
})
product.price = 10 // ok
product.price = -5 // type & validation error (fails NUMBER({ min: 0 }))
product.name = 'Updated name' // ok
product.name = 123 // type & validation error (inferred string type)
`
You can also simply validate different objects.
`typescript
import { BOOLEAN, INTEGER, Meta, STRING } from 'metatyper'
// Schema for validating incoming "create user" payloads
const createUserSchema = {
id: INTEGER({ min: 1 }),
username: STRING({
minLength: 3,
regexp: '^[a-zA-Z0-9 _]+$'
}),
age: INTEGER({ min: 18 }),
isAdmin: BOOLEAN({ optional: true })
}
// Example: validating an incoming HTTP/JSON payload
const badPayload = {
id: 0,
username: 'jd',
age: 16
}
let error = Meta.validate(createUserSchema, badPayload)
if (error) {
console.error('User payload is invalid:')
for (const issue of error.issues) {
const path = issue.path.join('.') || '(root)'
console.error( - field "${path}" failed with code "${issue.code}", value:, issue.value)
}
}
/*
Console log
User payload is invalid:
- field "id" failed with code "Min", value: 0
- field "username" failed with code "MinLength", value: jd
- field "age" failed with code "Min", value: 16
*/
const goodPayload = {
id: 1,
username: 'John_Doe',
age: 25,
isAdmin: false
}
error = Meta.validate(createUserSchema, goodPayload)
// === undefined (no validation issues)
if (!error) {
console.log('ok')
}
`
Finally, you can work with classes instead of objects.
`typescript
import 'reflect-metadata'
import { BOOLEAN, DATE, INTEGER, Meta, STRING, ValidationError } from 'metatyper'
// Domain model with runtime-checked properties
@Meta.Class()
class User {
@Meta.declare(INTEGER({ min: 1 })) // runtime type as decorator
id: number
@Meta.declare({ minLength: 3 }) // runtime type as decorator with reflection
username = 'Anonymous user'
isAdmin = BOOLEAN({ default: false }) // runtime type as value
createdAt = DATE({
default: new Date(),
coercion: true // cast timestamp/string from DB/API to Date (and back)
}) // runtime type cast
}
const user = new User()
// Typical use‑case: hydrate instance from plain data (e.g. API/DB)
try {
Meta.deserialize(user, {
id: 42,
username: 'Alice',
createdAt: 1704067200 * 1000,
// this timestamp will cast to Date("2024-01-01")
isAdmin: true
})
} catch (error) {
if (error instanceof ValidationError) {
console.error('Failed to deserialize User: ValidationError')
} else {
console.error('Unexpected error while deserializing User:', error)
}
}
Object.assign(user, {
username: 'Updated name'
}) // ok
try {
user.id = 0 // validation error (id must be >= 1)
} catch (error) {
if (error instanceof ValidationError) {
console.error('Failed to update User instance: ValidationError')
} else {
console.error('Unexpected error while updating User:', error)
}
}
`
- Introduction
- Installation
- Basic Usage
- Table of Contents
- Documentation
- Meta Objects
- Meta
- Meta args
- Meta inheritance
- Meta.Class decorator
- Meta.declare decorator
- Meta.isMetaObject
- Meta.isIgnoredProp
- Meta.copy
- Meta.rebuild
- Meta Types
- MetaType
- MetaType Implementation
- MetaTypeArgsType
- Built-in Meta Types
- ANY
- BOOLEAN
- STRING
- NUMBER
- INTEGER
- BIGINT
- DATE
- LITERAL
- INSTANCE
- UNION
- ARRAY
- TUPLE
- OBJECT
- Recursive structures
- Validation
- Validators
- Disabling Validation
- Serialization and Deserialization
- Serializers and Deserializers
- Disable Serialization
- Types Coercion
- BOOLEAN coercion
- STRING coercion
- NUMBER coercion
- INTEGER coercion
- BIGINT coercion
- DATE coercion
- Errors
- MetaTypeSerializationError
- MetaTypeSerializerError
- MetaTypeDeSerializerError
- MetaTypeValidatorError
- ValidationError
- API Reference
- Similar Libraries
- Change Log
#### Meta
Signature: Meta()
To work with Meta types it is convenient to use Meta objects.
A Meta object is a proxy object that changes the logic of reading and writing values to the object's properties.
Example:
`typescript
const objA = {
a: 1
}
const metaObjA = Meta(objA)
// throws a validation error because this property was initialized with a number
metaObjA.a = 'str'
const metaObjB = Meta()
metaObjB.a = 'str'
// throws a validation error because this property was initialized with a string
metaObjB.a = 2
`
Since classes are more often used to describe properties, this library provides Meta classes. The difference from Meta objects is that instances of the class will also be Meta objects.
Example:
`typescript
class A {
a = 'string'
static staticA = 2
}
const MetaA = Meta(A) // similar to the @Meta.Class() decorator
// throw a validation error because this property was initialized number
MetaA.staticA = 'str' as any
const metaInstanceA = new MetaA()
// throw a validation error because this property was initialized 'string'
metaInstanceA.a = 1 as any
`
> To get an original object from Meta object you can use Meta.proto(metaObject) method.
#### Meta args
Signature: MetaArgsType
These are the arguments for creating a Meta object.
`typescript`
function Meta
Meta function has the following arguments:
`typescript
type MetaArgsType = {
name?: string
initialValues?: Record
ignoreProps?: (string | symbol)[] | ((propName: string | symbol) => boolean)
safe?: boolean // default true
changeHandlers?: MetaChangeHandlerInfoType[]
errorHandlers?: MetaErrorHandlerInfoType[]
validationIsActive?: boolean
serializationIsActive?: boolean
metaTypesArgs?: MetaTypeArgsType | ((metaTypeImpl: MetaTypeImpl) => MetaTypeArgsType)
metaTypesResolver?: MetaTypesResolver
autoResolveMetaTypes?: boolean
dynamicDeclarations?: boolean
metaInstanceArgs?: MetaArgsType | 'same'
buildMetaInstance?: boolean
metaBuilder?: MetaObjectsBuilder
} & Record
`
- name?: string - A string that overrides the default name of the Meta object. The name is used when displaying the Meta object. For example, if the default name is MetaObject, you can pass MyMetaObject as the name argument to change it.
- initialValues?: Record - An object that defines the initial values of the properties of the Meta object.default
The value is {}.
- ignoreProps?: (string | symbol)[] | ((propName: string | symbol) => boolean) - Specifies which properties of the Meta object should be ignored by the Meta object. The default value is [].
It can be either:
- An array of strings or symbols that represent the property names to ignore.
- A function that takes a property name as an argument and returns a boolean value indicating whether to ignore it or not.
- safe?: boolean - Controls data integrity enforcement for all fields in the meta object.true
When (default), validation errors are thrown immediately.false
When , invalid data can be written to object fields without throwing;errorHandlers
errors can be handled via event handling mechanisms instead (see ).
- changeHandlers?: MetaChangeHandlerInfoType[] - An array of handlers that handle changes in the Meta object.default
The value is [].
- errorHandlers?: MetaErrorHandlerInfoType[] - An array of handlers that handle errors in the Meta object.default
The value is [].
- validationIsActive?: boolean - A boolean that indicates whether the Meta object should perform validation on the Meta object or not.default
The value is true.
- serializationIsActive?: boolean - A boolean that indicates whether the Meta object should perform serialization on the Meta object or not.default
The value is true.
- metaTypesArgs?: MetaTypeArgsType | ((metaTypeImpl: MetaTypeImpl) => MetaTypeArgsType) - Defines the arguments for building or rebuilding the Meta types of the Meta object. It can be either:default
- An object that contains the properties and values of the Meta types arguments.
- A function that takes a Meta type implementation as an argument and returns an object of Meta types arguments.
The value is {}.
- metaTypesResolver?: MetaTypesResolver - A function that resolves Meta types from values. It takes any value as an argument and returns a Meta type: (value: any, args?: MetaTypeArgsType) => MetaTypeImpl.default
The value is MetaTypeImpl.getMetaTypeImpl
- autoResolveMetaTypes?: boolean - A boolean that indicates whether the Meta object should automatically resolve the Meta types from a value or not.Meta({field: 1 })
For example, the value 1 in the object will be used to declare a NUMBER metatype.default
The value is true.
- dynamicDeclarations?: boolean - A boolean that indicates whether the Meta object should allow new declarations of new properties or not.metaObject.anyField = NUMBER({ default: 1 })
For example, you can define a new metatype like this: .default
The value is true.
- metaInstanceArgs?: MetaArgsType | 'same' - Defines the arguments for creating the Meta instance of the Meta class. It can be either:'same'
- An object that contains the properties and values of the Meta instance arguments.
- The string that indicates that the same arguments as the Meta class should be used.default
The value is 'same'.
- buildMetaInstance?: boolean - A boolean that indicates whether the Meta class should build the Meta instance or not. If true, the Meta class will use the metaInstanceArgs object to create the Meta instance.default
The value is true.
- metaBuilder?: MetaObjectsBuilder - A Meta objects builder that is used to create the Meta object. The Meta objects builder is an object that implements the MetaObjectsBuilder interface.default
The value is the global Meta objects builder (MetaObjectsBuilder.instance).
> To get the Meta args of a Meta object you can use Meta.getMetaArgs(metaObject) method.
#### Meta inheritance
Meta objects support the extension of classic objects with an additional side effect.
Object inheritance
`typescript
import { BOOLEAN, Meta, MetaType, NUMBER, STRING } from 'metatyper'
const obj1: any = {
a: 1,
b: NUMBER({ optional: true })
}
const obj2: any = {
c: 2,
d: STRING({ optional: true })
}
const obj3: any = {
e: 3,
f: BOOLEAN({ optional: true })
}
Object.setPrototypeOf(obj2, obj1)
const metaObj2 = Meta(obj2)
// true,
// because obj1 is not a Meta object
// and there is no special logic for its properties
console.log(metaObj2.b instanceof MetaType)
// but this will create its own MetaObj2 property 'a'
// and add a Meta type declaration NUMBER({ default: 0 })
metaObj2.b = NUMBER({ default: 0 })
console.log(metaObj2.b === 0) // true
Object.setPrototypeOf(obj3, metaObj2)
obj3.e = '1' // ok, because obj3 is not a Meta object
// validation error,
// because this is a property of the MetaObj2 Meta object
// and there is special logic for it
obj3.c = '1'
`
> The prototype of a Meta object is the original object: Object.getPrototypeOf(metaObj2) === obj2
Class inheritance
`typescript
import { Meta, NUMBER } from 'metatyper'
@Meta.Class()
class A {
static a = NUMBER({ optional: true })
a = NUMBER({ optional: true })
}
class B extends A {
static b = NUMBER({ optional: true })
b = NUMBER({ optional: true })
}
@Meta.Class()
class C extends B {
static c = NUMBER({ optional: true })
c = NUMBER({ optional: true })
}
console.log(A.toString())
// [ class A] { a: NUMBER = undefined }
console.log(B.toString())
// [ class B] { b = NUMBER }
console.log(C.toString())
// [ class C] { c: NUMBER = undefined; [a]: NUMBER = undefined }
// brackets [a] mean that property is a property of the parent Meta class
const aInstance = new A()
const bInstance = new B()
const cInstance = new C()
console.log(aInstance.toString())
// [ instance A] { a: NUMBER = undefined }
console.log(bInstance.toString())
// [ object] { a: NUMBER = undefined; b: NUMBER = undefined }
console.log(cInstance.toString())
// [ instance C] { a: NUMBER = undefined; b: NUMBER = undefined; c: NUMBER = undefined }
// There are no [a] brackets in instances,
// as these properties are intrinsic
// (you can learn how js instance creation works)
`
> Static classes work as simple Meta objects.
#### Meta.Class decorator
Signature: Meta.Class()
Decorator does the same thing as Meta(A).
Example:
`typescript
import { Meta } from 'metatyper'
@Meta.Class() // Meta.Class(args) has arguments as in Meta({}, args)
class MetaA {
a = 'string'
static a = 2
}
// throws a validation error because this property was initialized with a number
MetaA.a = 'str'
const metaInstanceA = new MetaA()
// throws a validation error because this property was initialized with a string
metaInstanceA.a = 1
`
#### Meta.declare decorator
Signature: Meta.declare()
This decorator lets you specify the Meta type of your properties.
You can do this in different ways:
- Specify the Meta type explicitly:
`typescript`
class Test {
@Meta.declare(NUMBER({ min: 0 }))
a: number
}
- Let the decorator infer the Meta type from the property value.
`typescript`
class Test {
@Meta.declare({ min: 0 })
a: number = 0
}
- Use reflect-metadata to automatically resolve the Meta type from the property type:
`typescript`
class Test {
@Meta.declare({ min: 0 })
a: number
}
> You need to import reflect-metadata before using the option. Otherwise, the Meta type will be ANY().
#### Meta.isMetaObject
Signature: Meta.isMetaObject()
If you need to check if an object is a Meta object, you can use this method: Meta.isMetaObject(obj).
#### Meta.isIgnoredProp
Signature: Meta.isIgnoredProp()
If you need to check if an property is ignored by Meta, you can use this method: Meta.isIgnoredProp(obj, 'propName').
#### Meta.copy
Signature: Meta.copy()
Sometimes you may need to copy a Meta object. You can do this by using the spread operator: { ...metaObject }. However, this will not copy the type declarations of the Meta object. To copy the type declarations as well, you can use Meta.copy:
`typescript`
const metaObjectCopy = Meta.copy(metaObject)
This method creates a copy of a Meta object and preserves its values, types, prototype and arguments.
Example:
`typescript
import { Meta, STRING } from 'metatyper'
const origObject: any = { a: 1 }
const origMetaObject = Meta(origObject)
origMetaObject.a = 2
origMetaObject.b = STRING({ default: '' })
const metaObjectCopy = Meta.copy(origMetaObject)
metaObjectCopy.a === 2 // true
metaObjectCopy.b === '' // true
`
#### Meta.rebuild
Signature: Meta.rebuild()
You may also need to reset the meta object to its original state.
The Meta.rebuild is useful for creating a new instance of a Meta object with its initial state and configuration.
`typescript`
const newMetaObject = Meta.rebuild(metaObject)
This method rebuilds a Meta object using the same original object and arguments that were used to create the Meta object.
Example:
`typescript
import { Meta, STRING } from 'metatyper'
const origObject: any = { a: 1 }
const origMetaObject = Meta(origObject)
origMetaObject.a = 2
origMetaObject.b = STRING({ default: '' })
const newMetaObject = Meta.rebuild(origMetaObject)
newMetaObject.a === 1 // true
newMetaObject.b === undefined // true
// because origObject was used to create the new Meta object`
#### MetaType
Signature: MetaType()
Meta types extend built-in types, but they have more features: validation and serialization.
The basic logic of Meta types is in metaTypeImpl.
Example, how to create a new Meta type:
`typescript
import { MetaType, StringImpl } from 'metatyper'
const newType1 = MetaType
/ metaTypeArgs /
})
const newType2 = MetaType
StringImpl.build({
/ metaTypeArgs /
})
)
`
#### MetaType Implementation
Signature: MetaTypeImpl
Meta type implementation example:
`typescript
import { MetaType, StringImpl } from 'metatyper'
class LowerCaseStringImpl extends StringImpl {
static isCompatible(value: string) {
if (!super.isCompatible(value)) {
return false
}
return !/[A-Z]/.test(value)
}
}
export function LowerCaseString() {
return MetaType
}
type LowerCaseString = MetaType
`
`typescript
import { Meta } from 'metatyper'
@Meta.Class()
class MyNewExample {
str = LowerCaseString()
}
const instance = new MyNewExample()
instance.str = 'abc' // ok
instance.str = 'aBc' // type and validation error
`
To learn more about the principles of Meta types creation, you can explore the source code of the built-in Meta types.
#### MetaTypeArgsType
Signature: MetaTypeArgsType
This represents the arguments for creating a Meta type.
`typescript
type MetaTypeArgsType<
T = any,
IsNullishT extends boolean = boolean,
IsNullableT extends boolean = IsNullishT,
IsOptionalT extends boolean = IsNullishT
> = {
name?: string
subType?: any
default?: T | ((declaration?: MetaTypeImpl) => T)
nullish?: IsNullishT
nullable?: IsNullableT
optional?: IsOptionalT
coercion?: boolean
validateType?: boolean
noBuiltinValidators?: boolean
noBuiltinSerializers?: boolean
noBuiltinDeSerializers?: boolean
validators?: (ValidatorType | ValidatorFuncType)[]
serializers?: (SerializerType | SerializeFuncType)[]
deserializers?: (DeSerializerType | DeSerializeFuncType)[]
safe?: boolean
} & Record
`
name?: string - A string that overrides the default name of the Meta type. The name is used when displaying the Meta type.
subType?: any - A Meta type or a value that defines the type of the nested values in the value.
For example, if the value is an array, you can use the subType to specify the type of the elements in the array.
default?: T | ((declaration?: MetaTypeImpl) => T) - A value or a function that returns a value that is used as the default value for the Meta type.undefined
The default value is used when the initial value is .
nullish?: boolean - A boolean indicating whether the value can be null or undefined.false
If , a NullableValidator and an OptionalValidator are added to the Meta type. The default value is false.
nullable?: boolean - A boolean indicating whether the value can be null.false
If , a NullableValidator is added to the Meta type. If nullish and nullable are contradictory,nullable
the value of will be chosen. Default value is the same as nullish.
optional?: boolean - A boolean indicating whether the value can be undefined. If false, an OptionalValidator is added to the Meta type.nullish
If and optional are contradictory, the value of optional will be chosen. Default value is the same as nullish
coercion?: boolean - A boolean that indicates whether the value should be coerced to the expected type or not.true
If , a CoercionSerializer is added to the Meta type, which tries to convert the main value to the appropriate type.
For example, if the Meta type is a string, and the main value is a number, the number will be cast to a string.
validateType?: boolean - A boolean that indicates whether the value should be validated against the expected type or not.true
If , a MetaTypeValidator is added to the Meta type, which checks that the main value matches the Meta type.true
Default value is .
noBuiltinValidators?: boolean - A boolean that indicates whether the built-in validators should be disabled or not.true
If , the Meta type will not use any of the default validators, like MetaTypeValidator or NullableValidator.false
Default value is .
noBuiltinSerializers?: boolean - A boolean that indicates whether the built-in serializers should be disabled or not.true
If , the Meta type will not use any of the default serializers, like CoercionSerializer.false
Default value is .
noBuiltinDeSerializers?: boolean - A boolean that indicates whether the built-in deserializers should be disabled or not.true
If , the Meta type will not use any of the default deserializers, like CoercionSerializer or ToLowerCaseSerializer (case argument in STRING).false
Default value is .
validators?: (ValidatorType | ValidatorFuncType)[] - An array of validators that are used to check the value when it is assigned to an object property.
`typescript
type ValidatorFuncType = (validateArgs: ValidatorArgsType) => boolean
type ValidatorType = {
name?: string
validate: ValidatorFuncType
}
`
> You can read about validation and ValidatorArgsType in the following section: Validation
serializers?: (SerializerType | SerializeFuncType)[] - An array of serializers that change the value when it is retrieved from the object.obj['prop']
For example, or Meta.serialize(obj).
`typescript
type SerializeFuncType = (serializeArgs: SerializerArgsType) => any
type SerializerType = {
serialize: SerializeFuncType
name?: string
serializePlaces?: ('get' | 'serialize' | 'unknown')[] | string[]
}
`
> You can read about serialization and SerializerArgsType in the following section: Serialization and Deserialization
deserializers?: (DeSerializerType | DeSerializeFuncType)[] - An array of deserializers that modify the value when it is set to an object property,obj['prop'] = 'value'
prior to validation. For example, or Meta.deserialize(metaObject, rawObject).
`typescript
type DeSerializeFuncType = (deserializeArgs: DeSerializerArgsType) => any
type DeSerializerType = {
deserialize: DeSerializeFuncType
name?: string
deserializePlaces?: ('init' | 'reinit' | 'set' | 'deserialize' | 'unknown')[] | string[]
}
`
> You can read about deserialization and DeSerializerArgsType in the following section: Serialization and Deserialization
safe?: boolean - Controls data integrity enforcement in meta objects. When true (default), validation errors are thrown immediately. When false, invalid data can be written to object fields without throwing; errors can be handled via event handling mechanisms instead (see Meta Args).
Each built-in Meta type has args?: MetaTypeArgsType as the last argument. You can see how to use it below.
#### ANY
Signature: ANY()
`typescript
import { ANY, Meta } from 'metatyper'
const obj1 = Meta({
a: ANY({ nullish: true })
}) // as { a: any | null | undefined }
obj1.a = 1
obj1.a = {}
`
#### BOOLEAN
Signature: BOOLEAN()
`typescript
import { BOOLEAN, Meta } from 'metatyper'
const obj = Meta({
someField: BOOLEAN({
default: false,
// BooleanMetaTypeArgs
// will replace 1 with true
trueValues: [1],
// will replace 0 with false
falseValues: [(value) => value === 0]
})
}) // as { someField: boolean }
obj.someField = true
obj.someField = 1 as boolean
obj.someField = 'true' // type & validation error
`
#### STRING
Signature: STRING()
`typescript
import { Meta, STRING } from 'metatyper'
const obj = Meta({
someField: STRING({
nullish: true,
// StringMetaTypeArgs
notEmpty: true, // alias for minLength: 1
maxLength: 10,
// validate using this regular expression
regexp: '^[a-zA-Z]+$',
// serialize to lowercase (or 'upper')
toCase: 'lower',
// trim whitespace from both ends of the string
trim: true
})
}) // as { someField?: string | null | undefined }
obj.someField = 'STR' // will serialize to lowercase
obj.someField = 1 // type & validation error
`
#### NUMBER
Signature: NUMBER()
`typescript
import { Meta, NUMBER } from 'metatyper'
const obj = Meta({
someField: NUMBER({
nullish: true,
// NumberMetaTypeArgs
min: 1, // value >= 1
max: 9, // value <= 9
greater: 0, // value > 0
less: 10 // value < 10
})
}) // as { someField?: number | null | undefined }
obj.someField = 1.2
obj.someField = 11 // validation error
obj.someField = 'str' // type & validation error
`
#### INTEGER
Signature: INTEGER()
`typescript
import { INTEGER, Meta } from 'metatyper'
const obj = Meta({
someField: INTEGER({
nullish: true,
// NumberMetaTypeArgs
min: 1, // value >= 1
max: 9, // value <= 9
greater: 0, // value > 0
less: 10 // value < 10
})
}) // as { someField?: number | null | undefined }
obj.someField = 1
obj.someField = 11 // validation error
obj.someField = 1.1 // validation error
`
#### BIGINT
Signature: BIGINT()
`typescript
import { BIGINT, Meta } from 'metatyper'
const obj = Meta({
someField: BIGINT({
nullish: true,
// NumberMetaTypeArgs
min: 1, // value >= 1
max: 9, // value <= 9
greater: 0, // value > 0
less: 10 // value < 10
})
}) // as { someField?: bigint | null | undefined }
obj.someField = 1n
obj.someField = 11n // validation error
obj.someField = 1 // type and validation error
`
#### DATE
Signature: DATE()
`typescript
import { DATE, Meta } from 'metatyper'
const obj = Meta({
someField: DATE({
nullish: true,
// DateMetaTypeArgs
min: 1, // value >= new Date(1)
max: new Date(), // value <= new Date()
greater: 0, // value > new Date(0)
less: 10n // value < new Date(10)
})
}) // as { someField?: Date | null | undefined }
obj.someField = new Date(1)
obj.someField = 1 // type and validation error
`
#### LITERAL
Signature: LITERAL()
`typescript
import { LITERAL, Meta } from 'metatyper'
const obj = Meta({
someField: LITERAL(1, {
nullish: true
})
}) // as { someField?: 1 | null | undefined }
obj.someField = 1
obj.someField = 2 // type and validation error
`
#### INSTANCE
Signature: INSTANCE()
`typescript
import { Meta, INSTANCE } from 'metatyper'
class A {
a = 1
}
class B extends A {
b = 2
}
const obj = Meta({
someField: INSTANCE(B, {
nullish: true
// InstanceMetaTypeArgs
// disallow the use of children B, default: true
allowChildren: false
})
}) // as { someField?: B | null | undefined }
obj.someField = new B() // ok
obj.someField = new A() // validation error
obj.someField = {} // type and validation error
obj.someField = B // type and validation error
`
#### UNION
Signature: UNION()
`typescript
import { BOOLEAN, Meta, STRING, UNION } from 'metatyper'
const obj = Meta({
someField: UNION([BOOLEAN({ nullable: true }), STRING({ optional: true })])
})
// as { someField: (boolean | null) | (string | undefined) }
obj.someField = true // ok
obj.someField = new Date() // type and validation error
`
#### ARRAY
Signature: ARRAY()
`typescript
import { Meta, ARRAY, BOOLEAN, STRING } from 'metatyper'
const obj = Meta({
someField: ARRAY(
[
BOOLEAN({ default: null, nullable: true }),
STRING({ optional: true })
],
{
nullish: true,
// ArrayMetaTypeArgs
notEmpty: true, // alias for minLength: 1
maxLength: 10,
// will create a frozen copy when deserializing
freeze: true
}
)
someField2: ARRAY(STRING(), { optional: true })
someField3: ARRAY(STRING())
})
/*
as {
someField:
| readonly (boolean | null | string | undefined)[]
| null
| undefined === undefined (because nullish)
someField2?: string[] === undefined (because optional)
someField3: string[] === [] (default value when not optional)
}
*/
obj.someField = ['1', '2'] // ok
obj.someField = [1, '1'] // type and validation error
`
#### TUPLE
Signature: TUPLE()
`typescript
import { Meta, STRING, TUPLE } from 'metatyper'
const obj = Meta({
someField: TUPLE([false, STRING({ optional: true })], {
nullish: true,
// TupleMetaTypeArgs
// will create a frozen copy when deserializing
freeze: true
})
})
/*
as {
someField:
| readonly [ boolean, string | undefined ]
| null
| undefined
}
*/
obj.someField = [true, '1'] // ok
obj.someField = ['1', true] // type and validation error
`
#### OBJECT
Signature: OBJECT()
`typescript
import { Meta, OBJECT, STRING, BOOLEAN } from 'metatyper'
const obj = Meta({
someField: OBJECT({
a: 1,
b: 'string',
c: BOOLEAN(),
d: {
e: STRING({ optional: true }),
f: OBJECT({})
}
}, {
nullish: true,
// ObjectMetaTypeArgs
// will create a frozen copy when deserializing
freeze: true,
// by default all fields are required
required: ['a', 'b', 'c'],
})
someField2: OBJECT(BOOLEAN(), { optional: true })
someField3: OBJECT(BOOLEAN()) // default {}, if not optional
})
/*
as {
someField: {
readonly a: number
readonly b: string
readonly c: boolean
readonly d?: {
e?: string
f: Record
}
}
someField2?: Record
someField3: Record
}
*/
obj.someField = {
a: 2,
b: 'str',
c: false,
d: {
// e: 'optional field'
f: {
anyField: true
}
}
}
obj.someField = {
a: 2,
b: 'str',
// type and validation error, c is not an optional field
}
obj.someField2 = {
anyField: true
}
`
#### Recursive structures
Signature: StructuralMetaTypeImpl
Meta types like OBJECT, ARRAY, TUPLE and UNION inherit from StructuralMetaTypeImpl.
This allows you to create recursive structures like this:
Use argument to create a REF
`typescript
import { Meta, OBJECT } from 'metatyper'
OBJECT((selfImpl) => {
type MyObjectType = {
// ... any fields
self: MyObjectType
}
return {
// ... any fields
self: selfImpl as any
} as MyObjectType
})
// OBJECT(g4dv1h)<{ self: REF
`
> Be careful, selfImpl is of type ObjectImpl, it's not a meta type
Use a variable to create a REF
`typescript
import { Meta, OBJECT } from 'metatyper'
type MyType = {
// ... any fields
self: MyType
}
const myType: OBJECT
return {
// ... any fields
self: myType
}
})
// OBJECT(g4dv1h)<{ self: REF
`
Use recursive structures to create a REF
`typescript
import { Meta, OBJECT } from 'metatyper'
type MyType = {
// ... any fields
self: MyType
}
const myTypeSchema: MyType = {
/ any fields /
}
myTypeSchema.self = myTypeSchema
OBJECT(myTypeSchema)
// OBJECT(g4dv1h)<{ self: REF
`
You can also create more complex recursive structures with nested references
`typescript
import { Meta, OBJECT, STRING, TUPLE } from 'metatyper'
const myObjectType = OBJECT((selfImpl) => {
type MyTuple = [MyObjectType, string, MyTuple]
type MyObjectType = {
num: number
obj: { selfImpl: MyObjectType }
arr: MyObjectType[]
tup: MyTuple
}
const myObjectSchema: any = {
num: 1,
obj: { selfImpl },
arr: null,
tup: TUPLE((selfImpl) => [myObjectType, STRING(), selfImpl])
}
myObjectSchema.arr = [myObjectSchema, selfImpl, myObjectType]
return myObjectSchema as MyObjectType
})
console.log(myObjectType.toString())
/*
OBJECT(n6f76)<{
num: NUMBER,
obj: OBJECT(jqrb3)<{ selfImpl: REF
arr: ARRAY(pugop)<
UNION(ljwth)<
REF
>[]
>,
tup: TUPLE(zwgxz)<[ REF
}>
*/
`
> REF - another optional type that works like a proxy.
Recursive values are not supported yet. You need to provide an undefined value instead of a recursive reference.
e.g.
`typescript
const obj = Meta({
innerObj: OBJECT((myObjImpl) => ({ myObj: myObjImpl }), { optional: true })
})
obj.innerObj = { myObj: { myObj: { myObj: undefined } } }
`
`typescript
const myObj = {
myObj: undefined
} as any
myObj.myObj = myObj
obj.innerObj = myObj // Maximum call stack size exceeded
`
Signature: EMAIL()
`typescript
import { Meta, EMAIL } from 'metatyper'
const obj = Meta({
email: EMAIL({
nullish: true,
// StringMetaTypeArgs (except custom regexp)
maxLength: 255,
trim: true
})
}) // as { email?: string | null | undefined }
obj.email = 'John' // validation error
obj.email = 'user@example.com' // ok
`
#### PHONE
Signature: PHONE()
`typescript
import { Meta, PHONE } from 'metatyper'
const obj = Meta({
phone: PHONE({
nullish: true
})
}) // as { phone?: string | null | undefined }
obj.phone = '12345' // validation error
obj.phone = '+1234567890' // ok
`
#### PASSWORD
Signature: PASSWORD()
The PASSWORD meta type is a specialized STRING with additional password‑oriented rules.StringMetaTypeArgs
It accepts all standard (such as nullable, optional, default,regexp, minLength, maxLength, trim, toCase, etc.) plus the following options:
- minLength?: number – minimum password length (defaults to 6 if not provided).
- requireLowercase?: boolean – require at least one lowercase letter [a-z]true
(defaults to , set to false to disable this check).
- requireUppercase?: boolean – require at least one uppercase letter [A-Z]true
(defaults to , set to false to disable this check).
- requireNumber?: boolean – require at least one digit [0-9]true
(defaults to , set to false to disable this check).
- requireSpecial?: boolean – require at least one non‑alphanumeric charactertrue
(defaults to , set to false to disable this check).
- confirmField?: string – when set, adds a validator that checks the value is equal"password"
to the value of the field with this name in the same object (useful for
/ "confirmPassword" forms).
`typescript
import { Meta, PASSWORD } from 'metatyper'
const obj = Meta({
password: PASSWORD({
minLength: 8,
requireLowercase: true,
requireUppercase: true,
requireNumber: true,
requireSpecial: true
}),
confirmPassword: PASSWORD({
confirmField: 'password'
})
}) // as { password: string; confirmPassword: string }
obj.password = 'short' // validation error (too short)
obj.password = 'password' // validation error (no number / special / upper)
obj.password = 'P@ssw0rd' // ok (meets complexity rules)
obj.confirmPassword = 'P@ss' // validation error (does not match password)
obj.confirmPassword = 'P@ssw0rd' // ok (matches password)
`
#### CARD
Credit card number validation using common brand patterns (Visa, MasterCard, American Express, Discover, Diners Club, JCB).
The validation pattern is adapted from regular-expressions.info.
Signature: CARD()
`typescript
import { Meta, CARD } from 'metatyper'
const obj = Meta({
card: CARD({
nullish: true
})
}) // as { card?: string | null | undefined }
obj.card = '1234567890123456' // validation error
obj.card = '4111111111111111' // ok
`
#### URL
Signature: URL()
`typescript
import { Meta, URL } from 'metatyper'
const obj = Meta({
website: URL({
nullish: true
})
}) // as { website?: string | null | undefined }
obj.website = 'not-a-url' // validation error
obj.website = 'https://user:pass@example.com/path/to/page.html?a=1#b=2' // ok
`
#### HOSTNAME
Signature: HOSTNAME()
`typescript
import { Meta, HOSTNAME } from 'metatyper'
const obj = Meta({
host: HOSTNAME({
nullish: true
})
}) // as { host?: string | null | undefined }
obj.host = 'not a host' // validation error
obj.host = 'example.com' // ok
`
#### IP
Signature: IP()
`typescript
import { Meta, IP } from 'metatyper'
const obj = Meta({
addr: IP({
nullish: true
})
}) // as { addr?: string | null | undefined }
obj.addr = '999.999.999.999' // validation error
obj.addr = '192.168.0.1' // ok
`
#### UUID
Signature: UUID()
`typescript
import { Meta, UUID } from 'metatyper'
const obj = Meta({
id: UUID({
nullish: true
})
}) // as { id?: string | null | undefined }
obj.id = 'not-uuid' // validation error
obj.id = '550e8400-e29b-41d4-a716-446655440000' // ok
`
#### SLUG
Signature: SLUG()
`typescript
import { Meta, SLUG } from 'metatyper'
const obj = Meta({
slug: SLUG({
nullish: true
})
}) // as { slug?: string | null | undefined }
obj.slug = 'Not A Slug' // validation error
obj.slug = 'my-blog-post-1' // ok
`
Meta objects come with built-in validation capability. Validators specific to each Meta type are utilized during the validation process. If validation fails, an exception is raised. For more information on validation errors, refer to the Errors section.
Validators for Meta types are categorized into:
- Built-in Validators: For example, STRING Meta type uses MinLengthValidator, configurable via the minLength argument.
- Runtime Validators: These are provided as arguments at runtime to the Meta type.
For more details on the arguments accepted by Meta types, see the MetaTypeArgsType section.
Validation occurs automatically when assigning new values to a Meta object. Additionally, you can explicitly validate another object using the Meta.validate helper:
`typescript`
Meta.validate(
metaObjectOrSchema: Meta
Example:
`typescript
import { Meta, STRING, ValidationError } from 'metatyper'
const schema = {
id: 0,
name: STRING({
validators: [
/ validators here /
]
})
}
const error = Meta.validate(schema, { id: '351', name: null })
if (error instanceof ValidationError) {
// each issue is MetaTypeValidatorError
for (const issue of error.issues) {
console.log(issue.code, issue.path, issue.value)
}
}
`
#### Validators
Signature: ValidatorType · ValidatorArgsType · ValidatorFuncType
A validator is an object that contains a validate method:
`typescript
type ValidatorType = {
name?: string
validate: (args: ValidatorArgsType) => boolean
}
type ValidatorArgsType = {
value: any
metaTypeImpl?: MetaTypeImpl
propName?: string | symbol
targetObject?: object
baseObject?: object
stopAtFirstError?: boolean
} & Record
`
- value: The value to be validated.metaTypeImpl
- : The Meta type implementation invoking this validator.propName
- : Specified when using Meta objects for validation.targetObject
- : The object that needs to be validated.baseObject
- : The object that contains the Meta type declaration with this validator.stopAtFirstError
- : Specifies if validation should cease after the first error. Defaults to true.
You can also validate standalone values directly via Meta types:
`typescript
const nameType = STRING({ minLength: 3 })
const error = nameType.validate('Jo')
// error is ValidationError | undefined
`
#### Disabling Validation
Validation can be disabled using the methods below:
Method 1
`typescript
Meta.validationIsActive(metaObj) === true
Meta.disableValidation(metaObj)
Meta.validationIsActive(metaObj) === false
metaObj.myProp = 0 // This is now allowed.
Meta.enableValidation(metaObj)
`
Method 2
`typescript
@Meta.Class()
class MyClass2 {
myProp = MyType({
validators: [],
// Set empty array
noBuiltinValidators: true
// Disables the built-in validators like MetaTypeValidator or MinLengthValidator
})
}
`
Serialization and deserialization of values are handled by Meta type's serializers and deserializers. Serialization is performed when retrieving a property’s value, whereas deserialization occurs during value assignment. Direct invocation of serialization and deserialization is also supported through specific methods:
`typescript`
Meta.serialize = (
metaObject: Meta
serializeArgs?: {
metaArgs?: MetaArgsType
}
): { [key in keyof T]: any }
> You can specify the serialize result type: Meta.serialize<{ a: number }>(...)
`typescript`
Meta.deserialize = (
metaObjectOrProto: Meta
rawObject: object,
deserializeArgs?: {
metaArgs?: MetaArgsType
}
): Meta
Example:
`typescript
import { Meta, DATE } from 'metatyper'
const objToSerialize = { id: '351', date: new Date(123) }
const objToDeSerialize = Meta.serialize<{
id: string
date: number
}>({ id: '', date: DATE({ coercion: true }) }, objToSerialize)
// objToDeSerialize = { id: '351', date: 123 }
Meta.deserialize({ id: '', date: DATE({ coercion: true }) }, objToDeSerialize) // -> Meta({ id: '351', date: new Date(123) })
`
#### Serializers and Deserializers
Signature: SerializerType · SerializerArgsType · DeSerializerType · DeSerializerArgsType
A serializer is an object with a serialize method, and a deserializer likewise has a deserialize method:
`typescript
type SerializerArgsType = {
value: any
metaTypeImpl?: MetaTypeImpl
propName?: string | symbol
targetObject?: object
baseObject?: object
place?: SerializePlaceType
}
type SerializerType = {
serialize: (serializeArgs: SerializerArgsType) => any
name?: string
serializePlaces?: SerializePlaceType[]
}
`
`typescript
type DeSerializerArgsType = {
value: any
metaTypeImpl?: MetaTypeImpl
propName?: string | symbol
targetObject?: object
baseObject?: object
place?: DeSerializePlaceType
}
type DeSerializerType = {
deserialize: (deserializeArgs: DeSerializerArgsType) => any
name?: string
deserializePlaces?: DeSerializePlaceType[]
}
`
Each field's purpose in both serializers and deserializers closely aligns with those in validators, specifying the object context and invoking Meta type implementation.
Serialization and deserialization processes can be adjusted depending on their specific use cases with the help of SerializePlaceType and DeSerializePlaceType. This allows for a more precise control over how and where these processes occur. The term "place" refers to the specific scenario in which serialization occurs.
SerializePlaceType indicates various contexts where serialization can happen:
- get: This is used when retrieving a property, for example, accessing a property like obj.prop.serialize
- : This context is applied during the serialization of an object, such as when using Meta.serialize(obj).unknown
- : This default setting is used for custom serialization logic that does not fit the other predefined contexts.
Similarly, DeSerializePlaceType outlines different scenarios for deserialization:
- init: Indicates the initialization of a new type declaration, like starting a Meta object with Meta({ prop: 'value' }).reinit
- : Used when re-initializing type declarations by defining a new Meta type, for example, changing a property's type with obj.prop = NUMBER().set
- : Applies when setting a new property value, such as obj.prop = 1.deserialize
- : This context is for deserializing an object, done like Meta.deserialize(obj, rawData).unknown
- : The default setting for custom deserialization logic that doesn't align with the specified contexts.
#### Disable Serialization
To disable serialization or deserialization:
Method 1
`typescript
Meta.serializationIsActive(metaObject) === true
Meta.disableSerialization(metaObject)
Meta.serializationIsActive(metaObject) === false
metaObject.myProp = 0 // Deserialization does not occur.
Meta.enableSerialization(metaObject)
`
Method 2
`typescript
@Meta.Class()
class MyClass2 {
myProp = MyType({
serializers: [],
// Empty array disables serializers
deserializers: [],
// Empty array disables deserializers
noBuiltinSerializers: true,
// Disables all built-in serializers like Coercion
noBuiltinDeSerializers: true
// Disables all built-in deserializers like Coercion
})
}
`
#### Types Coercion
Signature: MetaTypeArgsType
Meta types have coercion capabilities upon serialization and deserialization.
This is particularly handy for handling various value types such as dates in JSON data.
> undefined and null values will not be converted
These built-in metatypes support coercion:
##### BOOLEAN coercion
`typescript`
BOOLEAN({ coercion: true })
Meta.deserialize will cast value to !!value.
##### STRING coercion
`typescript`
STRING({ coercion: true })
Meta.deserialize will cast any value to string.
> For the date value, value.toISOString() will be used
##### NUMBER coercion
`typescript`
NUMBER({ coercion: true })
Meta.deserialize will cast value depending on the type of the value:
- Date -> value.getTime()bigint
- -> Number(value)string
- -> Number(value)boolean
- -> Number(value)
##### INTEGER coercion
`typescript`
INTEGER({ coercion: true })
Meta.deserialize will cast value depends on the type of the value:
- Date -> value.getTime()bigint
- -> Number(value)string
- -> Number(value)boolean
- -> Number(value)number
- -> Math.trunc(value)
##### BIGINT coercion
`typescript`
BIGINT({ coercion: true })
Meta.deserialize will cast value depends on the type of the value:
- Date -> BigInt(value.getTime())string
- -> BigInt(value)boolean
- -> BigInt(value)number
- -> BigInt(Math.trunc(value))
Meta.serialize will cast value to string.
##### DATE coercion
`typescript`
DATE({ coercion: true })
Meta.deserialize will cast value depends on the type of the value:
- bigint -> new Date(Number(value))number
- -> new Date(value)string
- -> new Date(value)
Meta.serialize will cast value to timestamp (value.getTime()).
When working with Meta objects, you may encounter a range of errors. These can be broadly categorized into standard errors, such as TypeError('Cannot assign to read only property ...'), and specialized errors unique to the handling of Meta objects. Understanding these errors and their hierarchy is crucial for effectively managing exceptions and maintaining robust code.
Specialized errors fall under the umbrella of MetaError and mainly cover:
- Validation – ValidationError (group of errors) and MetaTypeValidatorError (a single validator failure).MetaTypeSerializerError
- Serialization/Deserialization – and MetaTypeDeSerializerError.
This allows you to catch all MetaTyper-specific issues with instanceof MetaError, while still distinguishing between validation and serialization problems when needed.
#### MetaTypeSerializationError
Signature: MetaTypeSerializationError
MetaTypeSerializationError serves as an extended version of MetaError, focusing specifically on the identification and handling of serialization errors. Its main purpose is to allow developers to pinpoint serialization issues distinctively using instanceof checks. This differentiation is crucial for separating serialization errors from others, like validation errors, enhancing debugging efficiency.
All concrete serialization-related errors (MetaTypeSerializerError and MetaTypeDeSerializerError) extend this base class.
This means you can:
- catch any serialization problem via error instanceof MetaTypeSerializationError, andMetaTypeSerializerError
- still distinguish direction when necessary using (serialize, read, get) or MetaTypeDeSerializerError (deserialize, write, set).
#### MetaTypeSerializerError
Signature: MetaTypeSerializerError
When it comes to serialization of Meta type data, encountering errors is a possibility. The MetaTypeSerializerError is thrown when a serializer fails while producing an output value (for example, on coercion or in a custom serializer). This error aims to simplify the debugging process and error handling by offering in-depth information about where and why the failure occurred.
You can expect this error when:
- reading values from a Meta object while serialization is active (property access metaObj.prop),Meta.serialize(metaObj)
- calling / MetaType.serialize(value),place: 'get' | 'serialize' | 'unknown'
- or any other place where serializers run with .
Key fields of the MetaTypeSerializerError:
- serializer: This property provides a direct link to the SerializerType instance responsible for the error. This allows developers to easily identify which serializer was involved in the process and potentially inspect its configuration or state at the time of failure.
- serializerErrorArgs: Holding the type SerializerErrorArgsType, this property delivers a detailed look at the arguments fed into the serialization function at the error's occurrence. These arguments cover a range of information, from the value being serialized, the property's name, the target object, to additional options affecting serialization. Within serializerErrorArgs, there's a subError field holding an Error instance, shedding light on the precise cause of the serialization failure. This layered error reporting strategy significantly aids in debugging by providing a clear context of the error beyond merely indicating its occurrence.
`typescript
type SerializerErrorArgsType = {
value: any
subError?: Error
propName?: string | symbol
targetObject?: object
baseObject?: object
place?: SerializePlaceType
metaTypeImpl?: MetaTypeImpl
} & Record
`
Typical handling pattern:
`typescript
import { Meta, STRING, MetaTypeSerializerError } from 'metatyper'
const obj = Meta({
name: STRING({
serializers: [
{
name: 'CrashSerializer',
serialize() {
throw new Error('boom')
}
}
]
})
})
try {
const plain = Meta.serialize(obj)
} catch (error) {
if (error instanceof MetaTypeSerializerError) {
console.error('Serializer failed at place:', error.serializerErrorArgs.place)
console.error('Value:', error.serializerErrorArgs.value)
console.error('Inner error:', error.serializerErrorArgs.subError)
}
}
`
#### MetaTypeDeSerializerError
Signature: MetaTypeDeSerializerError
Mirroring the MetaTypeSerializerError, the MetaTypeDeSerializerError addresses errors during the deserialization of Meta type data. This exception is crucial for developers aiming to resolve issues arising from converting serialized data back into a usable form within the application.
You can encounter this error when:
- assigning values to Meta object properties (when deserializers are active),
- calling Meta.deserialize(metaObjOrSchema, rawObject),Meta.fromJson(...)
- using ,MetaType.deserialize(value)
- or inside .
Key fields of the MetaTypeDeSerializerError:
- deserializer: Reflecting the serializer attribute in MetaTypeSerializerError, this field links to the deserializer instance that encountered the error, facilitating an understanding of which deserialization logic didn't succeed.
- deserializerErrorArgs: As DeSerializerErrorArgsType, this attribute documents the arguments present at the deserialization function during the error event. Providing a comprehensive context, these arguments include the value being deserialized, relevant property names, and the objects involved, among others.
`typescript
type DeSerializerErrorArgsType = {
value: any
subError?: Error
propName?: string | symbol
targetObject?: object
baseObject?: object
place?: DeSerializePlaceType
metaTypeImpl?: MetaTypeImpl
} & Record
`
Example of handling deserialization failures:
`typescript
import { Meta, STRING, MetaTypeDeSerializerError } from 'metatyper'
const schema = {
name: STRING({
deserializers: [
{
name: 'CrashDeSerializer',
deserialize() {
throw new Error('bad input')
}
}
]
})
}
try {
Meta.deserialize(schema, { name: 'John' })
} catch (error) {
if (error instanceof MetaTypeDeSerializerError) {
console.error('Deserializer failed at place:', error.deserializerErrorArgs.place)
console.error('Raw value:', error.deserializerErrorArgs.value)
console.error('Inner error:', error.deserializerErrorArgs.subError)
}
}
`
#### MetaTypeValidatorError
Signature: MetaTypeValidatorError
During the validation process of Meta type data, it's possible to encounter failures. A MetaTypeValidatorError describes a _single_ validator issue and is typically exposed inside ValidationError.issues.
Key fields of the MetaTypeValidatorError:
- validator: Direct link to the ValidatorType that generated the error.ValidatorErrorArgsType
- validatorArgs: with detailed context (value, path, target/base objects, etc.).Error
- subError: Optional nested instance containing the original failure reason.name
- code: Short identifier for the validator (its or 'Unknown'). You can find all available codes in the built-in validators.['users', 0, 'name']
- path: Property path within the validated object (for example, ).
- value: Offending value.
This level of detail helps you build precise error messages and map validation problems back to UI fields.
#### ValidationError
Signature: ValidationError
ValidationError represents the group of validation errors aggregated for one operation.
In practice you will most often see it when:
- calling Meta.validate(...) – the function returns ValidationError | undefined.ValidationError
- assigning invalid values to a meta object – a may be thrown depending on the safe flag in Meta type arguments.
`typescript
import { Meta, STRING, ValidationError } from 'metatyper'
const user = Meta({
name: STRING({ minLength: 3 })
})
try {
user.name = 'Jo'
} catch (error) {
if (error instanceof ValidationError) {
for (const issue of error.issues) {
console.log(issue.code, issue.path, issue.value)
}
}
}
``

There are many other popular libraries that perform similar functions well. If you require specific functionality, you can explore various validation libraries (eg Zod) or type collections (eg Type-fest). Moreover, you have the flexibility to integrate the best features from these libraries alongside MetaTyper to achieve the best outcomes.
These libs are worth a look:
- class-validator
- io-ts
- joi
- ow
- runtypes
- ts-toolbelt
- type-fest
- yup
- zod
Stay updated with the latest changes and improvements: GitHub Releases.