@traversable/schema
    Preparing search index...

    Module @traversable/typebox - v0.0.13


    ᯓ𝘁𝗿𝗮𝘃𝗲𝗿𝘀𝗮𝗯𝗹𝗲/𝘁𝘆𝗽𝗲𝗯𝗼𝘅


    @traversable/typebox or box is a schema rewriter for TypeBox.

    NPM Version   TypeScript   License   npm  
    Static Badge   Static Badge   Static Badge  


    @traversable/typebox has a peer dependency on TypeBox (v0.34).

    $ pnpm add @traversable/typebox @sinclair/typebox
    

    Here's an example of importing the library:

    import * as T from '@sinclair/typebox'
    import { box } from '@traversable/typebox'

    // or, if you prefer, you can use named imports:
    import { deepClone, deepEqual } from '@traversable/typebox'

    // see below for specific examples

    box.deepClone lets users derive a specialized "deep copy" function that works with values that have been already validated.

    Because the values have already been validated, clone times are significantly faster than alternatives like window.structuredClone and Lodash.cloneDeep.

    Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

                               ┌─────────────────┐
    │ (avg) │
    ┌──────────────────────────┼─────────────────┤
    Lodash.cloneDeep │ 31.32x faster
    ├──────────────────────────┼─────────────────┤
    window.structuredClone │ 54.36x faster
    └──────────────────────────┴─────────────────┘

    This article goes into more detail about what makes box.deepClone so fast.

    import * as T from '@sinclair/typebox'
    import { box } from '@traversable/typebox'

    const Address = T.Object({
    street1: T.String(),
    street2: T.Optional(T.String()),
    city: T.String(),
    })

    const deepClone = box.deepClone(Address)
    const deepEqual = box.deepEqual(Address)

    const sherlock = { street1: '221 Baker St', street2: '#B', city: 'London' }
    const harry = { street1: '4 Privet Dr', city: 'Little Whinging' }

    const sherlockCloned = deepClone(sherlock)
    const harryCloned = deepClone(harry)

    deepEqual(sherlockCloned, sherlock) // => true
    deepEqual(harryCloned, harry) // => true

    sherlock === sherlockCloned // => false
    harry === harryCloned // => false

    box.deepClone.writeable lets users derive a specialized "deep clone" function that works with values that have been already validated.

    Compared to box.deepClone, box.deepClone.writeable returns the clone function in stringified ("writeable") form.

    import { box } from '@traversable/typebox'

    const deepClone = box.deepClone.writeable({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    }, { typeName: 'Address' })

    console.log(deepClone)
    // =>
    // type Address = { street1: string; street2?: string; city: string; }
    // function deepClone(prev: Address): Address {
    // return {
    // street1: prev.street1,
    // ...prev.street2 !== undefined && { street2: prev.street2 },
    // city: prev.city
    // }
    // }

    box.deepEqual lets users derive a specialized "deep equal" function that works with values that have been already validated.

    Because the values have already been validated, comparison times are significantly faster than alternatives like NodeJS.isDeepStrictEqual and Lodash.isEqual.

    Here's a Bolt sandbox if you'd like to run the benchmarks yourself.

                                 ┌────────────────┬────────────────┐
    Array (avg) │ Object (avg) │
    ┌────────────────────────────┼────────────────┼────────────────┤
    NodeJS.isDeepStrictEqual │ 40.3x faster │ 56.5x faster
    ├────────────────────────────┼────────────────┼────────────────┤
    Lodash.isEqual │ 53.7x faster │ 60.1x faster
    └────────────────────────────┴────────────────┴────────────────┘

    This article goes into more detail about what makes box.deepEqual so fast.

    • Best performance
    • Works in any environment that supports defining functions using the Function constructor, including (as of May 2025) Cloudflare workers 🎉
    import { box } from '@traversable/typebox'

    const deepEqual = box.deepEqual({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    })

    deepEqual(
    { street1: '221B Baker St', city: 'London' },
    { street1: '221B Baker St', city: 'London' }
    ) // => true

    deepEqual(
    { street1: '221B Baker St', city: 'London' },
    { street1: '4 Privet Dr', city: 'Little Whinging' }
    ) // => false

    box.deepEqual.writeable lets users derive a specialized "deep equal" function that works with values that have been already validated.

    Compared to box.deepEqual, box.deepEqual.writeable returns the deep equal function in stringified ("writeable") form.

    • Useful when you're consuming a set of TypeBox schemas and writing all them to disc somewhere
    • Also useful for testing purposes or for troubleshooting, since it gives you a way to "see" exactly what the deepEqual functions are doing
    import { box } from '@traversable/typebox'

    const deepEqual = box.deepEqual.writeable({
    type: 'object',
    required: ['street1', 'city'],
    properties: {
    street1: { type: 'string' },
    street2: { type: 'string' },
    city: { type: 'string' },
    }
    }, { typeName: 'Address' })

    console.log(deepEqual)
    // =>
    // type Address = { street1: string; street2?: string; city: string; }
    // function deepEqual(x: Address, y: Address) {
    // if (x === y) return true;
    // if (x.street1 !== y.street1) return false;
    // if (x.street2 !== y.street2) return false;
    // if (x.city !== y.city) return false;
    // return true;
    // }
    Note

    box.fold is an advanced API.

    Use box.fold to define a recursive traversal of a TypeBox schema. Useful when building a schema rewriter.

    Writing an arbitrary traversal with box.fold is:

    1. non-recursive
    2. 100% type-safe

    The way it works is pretty simple: if you imagine all the places in the TypeBox schema that are recursive, those "holes" will be the type that you provide via type parameter.

    As an example, let's write a function called check that takes a TypeBox schema, and returns a function that validates its input against the schema.

    Here's how you could use box.fold to implement it:

    import * as T from '@sinclair/typebox'
    import { box } from '@traversable/typebox'

    const isObject = (u: unknown): u is { [x: string]: unknown } =>
    !!u && typeof u === 'object' && !Array.isArray(u)

    const check = box.fold<(data: unknown) => boolean>((schema, original) => {
    // 𐙘_______________________𐙘
    // this type will fill the "holes" in our schema
    switch (true) {
    case box.isNull(schema):
    return (data) => data === null
    case box.isBoolean(schema):
    return (data) => typeof data === 'boolean'
    case box.isInteger(schema):
    return (data) => Number.isSafeInteger(data)
    case box.isNumber(schema):
    return (data) => Number.isFinite(data)
    case box.isArray(schema):
    return (data) => Array.isArray(data)
    && schema.every(schema.items)
    // 𐙘___𐙘
    // items: (data: unknown) => boolean
    case box.isObject(schema):
    return (data) => isObject(data)
    && Object.entries(schema.properties).every(
    // here we peek at the original schema to see if it's optional:
    ([key, property]) => box.isOptional(original.properties[key])
    // 𐙘______𐙘
    // property: (data: unknown) => boolean
    ? (!Object.hasOwn(data, key) || property(data[key]))
    : (Object.hasOwn(data, key) && property(data[key]))
    )
    default: return () => false
    }
    }
    )

    // Let's use `check` to create a predicate:
    const isBooleanArray = check(T.Array(T.Boolean))

    // Using the predicate looks like this:
    isBooleanArray([false]) // true
    isBooleanArray([true, 42]) // false

    That's it!

    If you'd like to see a more complex example, here's how box.check is actually implemented.

    box.fold is similar to, but more powerful than, the visitor pattern.

    If you're curious about the theory behind it, its implementation was based on a 1991 paper called Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire.

    Note

    box.Functor is an advanced API.

    box.Functor is the primary abstraction that powers @traversable/typebox.

    box.Functor is a powertool. Most of @traversable/typebox uses box.Functor under the hood.

    Compared to the rest of the library, it's fairly "low-level", so unless you're doing something pretty advanced you probably won't need to use it directly.

    Namespaces

    box
    check
    deepClone
    deepEqual
    F
    toString
    toType
    Type

    Interfaces

    CompilerIndex
    Index

    Type Aliases

    Algebra
    CompilerAlgebra
    DeepClonePrimitive
    Discriminated
    Options
    PathSpec
    Primitive
    RAISE_ISSUE_URL
    Tagged
    TypeName
    VERSION

    Variables

    compile
    CompilerFunctor
    defaultCompilerIndex
    defaultIndex
    defaultNextSpec
    defaultPrevSpec
    fold
    Functor
    Invariant
    isNullary
    RAISE_ISSUE_URL
    TypeName
    TypeNames
    VERSION

    Functions

    areAllObjects
    buildFunctionBody
    check
    deepClone
    deepCloneInlinePrimitiveCheck
    deepCloneIsPrimitive
    deepCloneSchemaOrdering
    deepEqual
    flattenUnion
    fromJson
    getTags
    inlinePrimitiveCheck
    isAny
    isArray
    isBigInt
    isBoolean
    isDate
    isInteger
    isIntersection
    isLiteral
    isNever
    isNull
    isNumber
    isNumeric
    isObject
    isOptional
    isPrimitive
    isRecord
    isScalar
    isSpecialCase
    isString
    isSymbol
    isTuple
    isTypelevelNullary
    isUndefined
    isUnion
    isUnknown
    isVoid
    schemaOrdering
    tagged
    toString
    toType