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
- Every field is checked before the object is trusted.
- The predicate turns
unknowninto your type. - This is what a schema library does, in twenty lines.
Keywords and builtins used here
IssueJSONNumberRunasconstfunctionifinterfaceisNaNnumberreturnstringtypeunknown
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.
Step 3 of 4 in Results & validation, step 7 of 19 in Classes & patterns.