typestar

Validating unknown data in TypeScript

Parsing at the boundary, so the inside of the program is typed.

interface Run {
  lang: string;
  tpm: number;
  stars?: number;
}

type Issue = { path: string; message: string };

function validateRun(input: unknown): { run: Run } | { issues: Issue[] } {
  const issues: Issue[] = [];
  const raw = (typeof input === "object" && input !== null ? input : {}) as
    Record<string, unknown>;

  if (typeof raw.lang !== "string" || raw.lang.length === 0) {
    issues.push({ path: "lang", message: "required string" });
  }
  if (typeof raw.tpm !== "number" || Number.isNaN(raw.tpm)) {
    issues.push({ path: "tpm", message: "required number" });
  }
  if (raw.stars !== undefined && typeof raw.stars !== "number") {
    issues.push({ path: "stars", message: "must be a number" });
  }

  return issues.length
    ? { issues }
    : { run: { lang: raw.lang as string, tpm: raw.tpm as number,
               stars: raw.stars as number | undefined } };
}

const parsed = validateRun(JSON.parse('{"lang":"ts","tpm":98}'));
console.log("run" in parsed ? parsed.run.lang : parsed.issues);
console.log(validateRun({ tpm: "fast" }));

How it works

  1. Every field is checked before the object is trusted.
  2. The predicate turns unknown into your type.
  3. This is what a schema library does, in twenty lines.

Keywords and builtins used here

The run, in numbers

Lines
32
Characters to type
998
Tokens
264
Three-star pace
105 tpm

At the three-star pace of 105 tokens a minute, this run takes about 151 seconds.

Type this snippet

Step 3 of 4 in Results & validation, step 7 of 19 in Classes & patterns.

← Previous Next →