10LOC

#type-guards

TypeScriptintermediate

A recursive type for JSON-safe values

type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };

const isJsonValue = (value: unknown): value is JsonValue => {
  if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return true;
  if (Array.isArray(value)) return value.every(isJsonValue);

Define exactly what JSON.stringify can round-trip, recursively, and use it to reject values like undefined, functions, and Dates.

TypeScriptintermediate

Assertion functions (asserts x is T) for runtime-checked narrowing

export function assertIsString(value: unknown, name: string): asserts value is string {
  if (typeof value !== "string") {
    throw new TypeError(`${name} must be a string, got ${typeof value}`);
  }
}

Narrow a value by throwing instead of branching, so the rest of the function can assume the checked type without an extra if.

TypeScriptintermediate

unknown plus a type guard instead of any for safe parsing

type ApiUser = { id: string; email: string };

const isApiUser = (value: unknown): value is ApiUser =>
  typeof value === "object" &&
  value !== null &&

Accept untyped input like JSON.parse's output without any, by narrowing it through a type predicate before touching its fields.