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.
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 },
}]
}
})