SchemaDecoder08 / 12

Validating data with a schema

One schema defines runtime validation and the TypeScript type.

SchemaDecoder validates input and returns data with the expected structure.

In schemas.ts, board width and height are validated as positive integers. Extract that check into BoardSchema for this example:

import { SchemaDecoder } from '@gamedevland/engine/validation';
import type { InferDecoded } from '@gamedevland/engine/validation';

export class BoardSchema {
  static readonly decoder = SchemaDecoder.object({
    width: SchemaDecoder.number({ integer: true, min: 1 }),
    height: SchemaDecoder.number({ integer: true, min: 1 }),
  });
}

export type BoardConfig = InferDecoded<typeof BoardSchema.decoder>;

InferDecoded derives the type from the schema: a separate interface with the same fields is not needed.

01
unknownFor example, { width: 8, height: "8" }
02
SchemaDecoder.decodeChecks fields, types and constraints
03
Result or errorThe string "8" does not become a number automatically
Decoding checks the data structure. Game domain logic later checks whether the move itself is valid.

Now validate the JSON data:

const board = SchemaDecoder.decode(raw, BoardSchema.decoder, 'board.json');

Here, raw is input of type unknown. The board.json label helps locate the source of an error.

The object, readonlyArray, string, number, boolean, record, optional and enumOf combinators describe nested structures. Constraints express valid values: for example, a slot number is an integer from 0 to 2. The domain model checks whether a piece fits into free cells, since that depends on the current board.

Sources ↗

Block Puzzle · event.schemas.ts↗The boundary between request structure and placement rules.