Schema.Advanced

The Schema submodule enables user-defined JSON Schema layouts and provides type inference beyond the types available through the Type.* compositors. The following example shows a hypothetical GraphicsModule containing several cross-dependent types that utilize the $defs keyword for reusable schema definitions. The implementation provides a custom import that merges the definitions with a $ref entry point. The type definitions and their corresponding functions are structured symmetrically.

⚠️ Native JSON Schema inference and validation is an evolving feature of the TypeBox library. Due to the highly flexible nature of schema layouts, TypeBox cannot provide a simplified interface for every possible layout. Consequently, leveraging native JSON Schema inference may require a moderate level of familiarity with type-level programming concepts.

GraphicsModule Reference Link

import Type from 'typebox'

// ------------------------------------------------------------------------------
// TImport<Module, Ref>
//
// Import definitions types from { $defs } schematic layouts.
// ------------------------------------------------------------------------------
export type TImport<
  Module extends { $defs: Type.TProperties }, 
  Ref extends string = Extract<keyof Module['$defs'], string>
> = (
  Module & { $ref: `#/$defs/${Ref}` }
)
export function Import<
  Module extends { $defs: Type.TProperties }, 
  Ref extends string = Extract<keyof Module['$defs'], string>
>(module: Module, ref: Ref): TImport<Module, Ref> {
  return { ...module, $ref: `#/$defs/${ref}` } as never
}

// ------------------------------------------------------------------------------
// Reference: GraphicsModule
//
// Layout for cross-dependent type definitions
// ------------------------------------------------------------------------------
export const GraphicsModule = {
  $defs: {
    Vector2: Type.Object({
      x: Type.Number(),
      y: Type.Number()
    }),
    Vector3: Type.Object({
      x: Type.Number(),
      y: Type.Number(),
      z: Type.Number()
    }),
    Vertex: Type.Object({
      position: Type.Ref('#/$defs/Vector3'),
      normal: Type.Ref('#/$defs/Vector3'),
      texcoord: Type.Ref('#/$defs/Vector2'),
    }),
    Geometry: Type.Object({
      vertices: Type.Array(Type.Ref('#/$defs/Vertex')),
      indices: Type.Array(Type.Integer())
    }),
    Material: Type.Object({
      ambient: Type.Ref('#/$defs/Vector3'),
      diffuse: Type.Ref('#/$defs/Vector3'),
      specular: Type.Ref('#/$defs/Vector3'),
    }),
    Mesh: Type.Object({
      geometry: Type.Ref('#/$defs/Geometry'),
      material: Type.Ref('#/$defs/Material'),
    })
  }
} as const

// ------------------------------------------------------------------------------
// Schematics and Inference
//
// We can derive schematics and inference in the following way
// ------------------------------------------------------------------------------

export type Vector2 = Type.Static<typeof Vector2>
export type Vector3 = Type.Static<typeof Vector3>
export type Vertex = Type.Static<typeof Vertex>
export type Geometry = Type.Static<typeof Geometry>
export type Material = Type.Static<typeof Material>
export type Mesh = Type.Static<typeof Mesh>

export const Vector2 = Import(GraphicsModule, 'Vector2')
export const Vector3 = Import(GraphicsModule, 'Vector3')
export const Vertex = Import(GraphicsModule, 'Vertex')
export const Geometry = Import(GraphicsModule, 'Geometry')
export const Material = Import(GraphicsModule, 'Material')
export const Mesh = Import(GraphicsModule, 'Mesh')

// ------------------------------------------------------------------------------
// Validation
// ------------------------------------------------------------------------------
import { Parse } from 'typebox/schema'

const mesh = Parse(Mesh, {
  material: {
    ambient: { x: 1, y: 2, z: 3 },
    diffuse: { x: 1, y: 2, z: 3 },
    specular: { x: 1, y: 2, z: 3 },
  },
  geometry: {
    indices: [0],
    vertices: [{
      position: { x: 1, y: 2, z: 3 },
      normal: { x: 1, y: 2, z: 3 },
      texcoord: { x: 1, y: 2 },
    }]
  }
})