npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

metatyper

v1.7.0

Published

The MetaTyper project provides a powerful approach to using runtime types in TypeScript and JavaScript code

Readme

MetaTyper

code faster, smarter, better


website npm license: MIT build size Tests Coveralls donate

 

Introduction

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.

 

Installation

npm install metatyper

or

yarn add metatyper

or

<script src="https://cdn.jsdelivr.net/npm/metatyper/lib/metatyper.min.js"></script>

<!-- or another cdn -->
<script src="https://unpkg.com/metatyper/lib/metatyper.min.js"></script>

In this case, you need to use this library through the MetaTyper global variable: MetaTyper.Meta({ /* ... */ })

 

Basic Usage

First, you can create a Meta object.

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.

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.

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)
    }
}

 

Table of Contents

 

Documentation

Meta Objects

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:

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:

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.

function Meta<T extends object>(protoObject?: T, metaArgs?: MetaArgsType): Meta<T>

Meta function has the following arguments:

type MetaArgsType = {
    name?: string
    initialValues?: Record<string | symbol, any>

    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<string, any>

 

  • 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<string | symbol, any> - An object that defines the initial values of the properties of the Meta object. The default 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. 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 errorHandlers).

 

  • changeHandlers?: MetaChangeHandlerInfoType[] - An array of handlers that handle changes in the Meta object. The default value is [].

 

  • errorHandlers?: MetaErrorHandlerInfoType[] - An array of handlers that handle errors in the Meta object. The default value is [].

 

  • validationIsActive?: boolean - A boolean that indicates whether the Meta object should perform validation on the Meta object or not. The default value is true.

 

  • serializationIsActive?: boolean - A boolean that indicates whether the Meta object should perform serialization on the Meta object or not. The default 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:
    • 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 default 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. The default 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. For example, the value 1 in the Meta({field: 1 }) object will be used to declare a NUMBER metatype. The default value is true.

 

  • dynamicDeclarations?: boolean - A boolean that indicates whether the Meta object should allow new declarations of new properties or not. For example, you can define a new metatype like this: metaObject.anyField = NUMBER({ default: 1 }). The default value is true.

 

  • metaInstanceArgs?: MetaArgsType | 'same' - Defines the arguments for creating the Meta instance of the Meta class. It can be either:
    • An object that contains the properties and values of the Meta instance arguments.
    • The string 'same' that indicates that the same arguments as the Meta class should be used. The default 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. The default 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. The default 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

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

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())
// [<meta> class A] { a: NUMBER = undefined }

console.log(B.toString())
// [<meta child> class B] { b = NUMBER }

console.log(C.toString())
// [<meta> 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())
// [<meta> instance A] { a: NUMBER = undefined }

console.log(bInstance.toString())
// [<meta> object] { a: NUMBER = undefined; b: NUMBER = undefined }

console.log(cInstance.toString())
// [<meta> 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:

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:
class Test {
    @Meta.declare(NUMBER({ min: 0 }))
    a: number
}

 

  • Let the decorator infer the Meta type from the property value.
class Test {
    @Meta.declare({ min: 0 })
    a: number = 0
}

 

  • Use reflect-metadata to automatically resolve the Meta type from the property type:
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:

const metaObjectCopy = Meta.copy(metaObject)

This method creates a copy of a Meta object and preserves its values, types, prototype and arguments.

Example:

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.

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:

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

 

Meta Types

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:

import { MetaType, StringImpl } from 'metatyper'

const newType1 = MetaType<string>(StringImpl, {
    /* metaTypeArgs */
})
const newType2 = MetaType<string>(
    StringImpl.build({
        /* metaTypeArgs */
    })
)

 

MetaType Implementation

Signature: MetaTypeImpl

Meta type implementation example:

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<LowerCaseString>(LowerCaseStringImpl)
}

type LowerCaseString = MetaType<Lowercase<string>, LowerCaseStringImpl>
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.

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<string, any>

 

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. The default value is used when the initial value is undefined.

 

nullish?: boolean - A boolean indicating whether the value can be null or undefined. If false, 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. If false, a NullableValidator is added to the Meta type. If nullish and nullable are contradictory, the value of nullable 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. If nullish 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. If true, 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. If true, a MetaTypeValidator is added to the Meta type, which checks that the main value matches the Meta type. Default value is true.

 

noBuiltinValidators?: boolean - A boolean that indicates whether the built-in validators should be disabled or not. If true, the Meta type will not use any of the default validators, like MetaTypeValidator or NullableValidator. Default value is false.

 

noBuiltinSerializers?: boolean - A boolean that indicates whether the built-in serializers should be disabled or not. If true, the Meta type will not use any of the default serializers, like CoercionSerializer. Default value is false.

 

noBuiltinDeSerializers?: boolean - A boolean that indicates whether the built-in deserializers should be disabled or not. If true, the Meta type will not use any of the default deserializers, like CoercionSerializer or ToLowerCaseSerializer (case argument in STRING). Default value is false.

 

validators?: (ValidatorType | ValidatorFuncType)[] - An array of validators that are used to check the value when it is assigned to an object property.

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. For example, obj['prop'] or Meta.serialize(obj).

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, prior to validation. For example, obj['prop'] = 'value' or Meta.deserialize(metaObject, rawObject).

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).

 

Built-in Meta Types

Each built-in Meta type has args?: MetaTypeArgsType as the last argument. You can see how to use it below.

 

ANY

Signature: ANY()

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()

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()

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()

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()

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()

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()

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()

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()

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()

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()

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()

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()

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<string, any>
        }
    }

    someField2?: Record<string, boolean> === undefined
    someField3: Record<string, boolean>
}
*/


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

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<g4dv1h> }>

Be careful, selfImpl is of type ObjectImpl, it's not a meta type

 

Use a variable to create a REF

import { Meta, OBJECT } from 'metatyper'

type MyType = {
    // ... any fields
    self: MyType
}

const myType: OBJECT<MyType> = OBJECT(() => {
    return {
        // ... any fields
        self: myType
    }
})
// OBJECT(g4dv1h)<{ self: REF<g4dv1h> }>

 

Use recursive structures to create a REF

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<g4dv1h> }>

 

You can also create more complex recursive structures with nested references

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<n6f76> }>, 
    arr: ARRAY(pugop)<
        UNION(ljwth)<
            REF<n6f76> | REF<n6f76> | REF<n6f76>
        >[]
    >, 
    tup: TUPLE(zwgxz)<[ REF<n6f76>, STRING, REF<zwgxz> ]> 
}>
*/

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.

const obj = Meta({
    innerObj: OBJECT((myObjImpl) => ({ myObj: myObjImpl }), { optional: true })
})

obj.innerObj = { myObj: { myObj: { myObj: undefined } } }
const myObj = {
    myObj: undefined
} as any

myObj.myObj = myObj

obj.innerObj = myObj // Maximum call stack size exceeded

 

EMAIL

Signature: EMAIL()

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 = '[email protected]' // ok

 

PHONE

Signature: PHONE()

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. It accepts all standard StringMetaTypeArgs (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] (defaults to true, set to false to disable this check).

  • requireUppercase?: boolean – require at least one uppercase letter [A-Z] (defaults to true, set to false to disable this check).

  • requireNumber?: boolean – require at least one digit [0-9] (defaults to true, set to false to disable this check).

  • requireSpecial?: boolean – require at least one non‑alphanumeric character (defaults to true, set to false to disable this check).

  • confirmField?: string – when set, adds a validator that checks the value is equal to the value of the field with this name in the same object (useful for "password" / "confirmPassword" forms).

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()

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()

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:[email protected]/path/to/page.html?a=1#b=2' // ok

 

HOSTNAME

Signature: HOSTNAME()

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()

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()

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()

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

 

Validation

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:

Meta.validate(
    metaObjectOrSchema: Meta<object> | object,
    rawObject: object,
    validateArgs?: {
        /** Stop collecting issues for a particular field at the first problem. */
        stopAtFirstError?: boolean
    }
): ValidationError | undefined

Example:

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:

type ValidatorType = {
    name?: string
    validate: (args: ValidatorArgsType) => boolean
}

type ValidatorArgsType = {
    value: any
    metaTypeImpl?: MetaTypeImpl
    propName?: string | symbol
    targetObject?: object
    baseObject?: object

    stopAtFirstError?: boolean
} & Record<string, any>
  • 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:

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

Meta.validationIsActive(metaObj) === true

Meta.disableValidation(metaObj)

Meta.validationIsActive(metaObj) === false

metaObj.myProp = 0 // This is now allowed.

Meta.enableValidation(metaObj)

Method 2

@Meta.Class()
class MyClass2 {
    myProp = MyType({
        validators: [],
        // Set empty array

        noBuiltinValidators: true
        // Disables the built-in validators like MetaTypeValidator or MinLengthValidator
    })
}

 

Serialization and Deserialization

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:

Meta.serialize = (
    metaObject: Meta<T> | T,
    serializeArgs?: {
        metaArgs?: MetaArgsType
    }
): { [key in keyof T]: any }

You can specify the serialize result type: Meta.serialize<{ a: number }>(...)

Meta.deserialize = (
    metaObjectOrProto: Meta<T> | T,
    rawObject: object,
    deserializeArgs?: {
        metaArgs?: MetaArgsType
    }
): Meta<T>

 

Example:

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:

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[]
}
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

Meta.serializationIsActive(metaObject) === true

Meta.disableSerialization(metaObject)

Meta.serializationIsActive(metaObject) === false

metaObject.myProp = 0 // Deserialization does not occur.

Meta.enableSerialization(metaObject)

Method 2

@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
BOOLEAN({ coercion: true })

Meta.deserialize will cast value to !!value.

 

STRING coercion
STRING({ coercion: true })

Meta.deserialize will cast any value to string.

For the date value, value.toISOString() will be used

 

NUMBER coercion
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
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
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
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()).

 

Errors

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:

  • ValidationValidationError (group of errors) and MetaTypeValidatorError (a single validator failure).
  • Serialization/DeserializationMetaTypeSerializerError 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, and
  • still distinguish direction when necessary using MetaTypeSerializerError (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),
  • calling Meta.serialize(metaObj) / MetaType.serialize(value),
  • or any other place where serializers run with place: 'get' | 'serialize' | 'unknown'.

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.

type SerializerErrorArgsType = {
    value: any

    subError?: Error

    propName?: string | symbol
    targetObject?: object
    baseObject?: object
    place?: SerializePlaceType

    metaTypeImpl?: MetaTypeImpl
} & Record<string, any>

Typical handling pattern:

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),
  • using Meta.fromJson(...),
  • or inside MetaType.deserialize(value).

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.

type DeSerializerErrorArgsType = {
    value: any

    subError?: Error

    propName?: string | symbol
    targetObject?: object
    baseObject?: object
    place?: DeSerializePlaceType

    metaTypeImpl?: MetaTypeImpl
} & Record<string, any>

Example of handling deserialization failures:

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.
  • validatorArgs: ValidatorErrorArgsType with detailed context (value, path, target/base objects, etc.).
  • subError: Optional nested Error instance containing the original failure reason.
  • code: Short identifier for the validator (its name or 'Unknown'). You can find all available codes in the built-in validators.
  • path: Property path within the validated object (for example, ['users', 0, 'name']).
  • 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.
  • assigning invalid values to a meta object – a ValidationError may be thrown depending on the safe flag in Meta type arguments.
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)
        }
    }
}

 

API Reference

API Reference

 

Similar Libraries

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

 

Change Log

Stay updated with the latest changes and improvements: GitHub Releases.