Software engineering
Validate data where it enters your application
A small TypeScript example for turning unknown input into a useful, predictable value.
By Thomas Watson · · 3 min read
A TypeScript type describes what your code expects. It does not check a request body, imported file or stored browser value at runtime. A value can arrive as anything, even when the code that consumes it expects a particular shape. Treat that transition as an explicit boundary.
Start with unknown
Suppose a saved game loadout contains a name and a list of item identifiers. Casting parsed JSON to a Loadout makes the compiler accept it, but it does not establish that either field exists. Instead, accept unknown and check the fields before returning a typed value.
type Loadout = { name: string; itemIds: string[] };
function parseLoadout(value: unknown): Loadout | null {
if (typeof value !== "object" || value === null) return null;
if (!("name" in value) || !("itemIds" in value)) return null;
if (typeof value.name !== "string") return null;
const name = value.name.trim();
if (name.length === 0 || name.length > 80) return null;
if (!Array.isArray(value.itemIds) || value.itemIds.length > 20) return null;
if (!value.itemIds.every((item: unknown) =>
typeof item === "string" && item.length > 0 && item.length <= 100
)) return null;
return { name, itemIds: [...value.itemIds] };
}Shape is only the first check
This parser checks structure and bounds the accepted values. It does not establish that those items exist, that the player owns them or that the combination is valid for a particular game version. Those are separate checks against authoritative data. A valid string is not permission to read or change the resource it names.
The length limits here are examples, not universal defaults. Pick limits that match your interface and storage. Bound the total request size before parsing too: rejecting an enormous array after loading it into memory does not protect that earlier step.
Test the boundary, not just the happy path
- Accept a valid loadout and check that its name is trimmed.
- Reject null, arrays without the required fields, missing fields and names made only of whitespace.
- Reject mixed item arrays, oversized names and too many items.
- Test the separate rules for missing items, ownership and incompatible combinations.
Return an error your caller can handle rather than allowing malformed data to fail several layers later. For larger shapes, a schema library can reduce repetition. The principle stays the same: validation belongs where the trust in the data changes.
