TypeScript
Use generated JSON-shaped types and optional runtime validation.
The TypeScript generator emits type aliases matching the JSON representation. Records use wire field names, and variants become unions selected by their configured tags. Wrappers and primitive opaque types use nominal brands to keep distinct types separate.
Generate and Import Types
Generate the task model and install its runtime:
sidex generate ts src/generated
npm install @sidex/types
Each schema produces a .ts file. index.ts exports schema modules. Import a type directly:
import type { Task } from "./generated/task";
const task: Task = {
title: "Write a schema",
completed: false,
};
const json = JSON.stringify(task);
No separate conversion is needed to serialize a value already matching the JSON representation.
Type Checking and Incoming Data
TypeScript type checking runs before execution. A type annotation or assertion does not validate JSON received at runtime:
const input: unknown = JSON.parse(responseText);
Use a runtime JSON Schema validator to check the structure of input against the generated JSON Schema. Once a value has a known shape, generated validation rules can check application constraints.
Nominal Types
A nominal type carries a compile-time identity even when its value is a string or number:
wrapper TaskId: string
wrapper UserId: string
This prevents assigning a TaskId where a UserId is expected. A brand supplies type safety; it does not change the JSON value or validate it at runtime.
Built-in numeric types also use brands in @sidex/types. Review that package’s numeric representations before introducing numeric fields. In particular, 64-bit values need an explicit cross-language policy; see numbers and bytes.
Enable Validation
[backend.ts]
plugins = ["validate"]npm install @sidex/validate
sidex generate ts src/generated
The type generator is always enabled, so list only additional plugins. Types and their validator objects share an exported name:
import { User } from "./generated/users";
const report = User.fields.username.validate("");
for (const error of report.errors) {
console.log(error.code, error.message);
}
See validation for the corresponding schema and whole-record validation.
Configure Imports and Mappings
External bundles can map to package imports:
[backend.ts.external]
identity = "@example/identity-models"
Type mappings use fully qualified Sidex paths:
[backend.ts.types.table]
"::core::builtins::i64" = "string"
A mapping changes a generated type expression. It does not add a serializer or make other applications adopt the same wire representation.
Supplying a types.table replaces the default mapping table. Include every built-in mapping your model requires. See the manifest reference before applying a custom table.