validate-config.ts in TypeScript
Parse unknown input into a fully typed config or issues.
#!/usr/bin/env node
// Parse and validate a config object into a fully typed shape.
interface Config {
host: string;
port: number;
debug: boolean;
tags: string[];
}
type Issue = { field: keyof Config; message: string };
type Parsed = { ok: true; config: Config } | { ok: false; issues: Issue[] };
function parseConfig(raw: Record<string, unknown>): Parsed {
const issues: Issue[] = [];
const host = typeof raw.host === "string" ? raw.host : "";
if (!host) issues.push({ field: "host", message: "must be a string" });
const port = typeof raw.port === "number" ? raw.port : NaN;
if (!Number.isInteger(port) || port < 1 || port > 65535) {
issues.push({ field: "port", message: "must be 1-65535" });
}
const debug = raw.debug === true;
const tags = Array.isArray(raw.tags)
? raw.tags.filter((t): t is string => typeof t === "string")
: [];
if (issues.length > 0) return { ok: false, issues };
return { ok: true, config: { host, port, debug, tags } };
}
const result = parseConfig({ host: "localhost", port: 8080, tags: ["dev"] });
if (result.ok) {
console.log(`serving ${result.config.host}:${result.config.port}`);
console.log(`tags: ${result.config.tags.join(", ") || "none"}`);
} else {
for (const issue of result.issues) {
console.error(`${issue.field}: ${issue.message}`);
}
process.exitCode = 1;
}
How it works
- A discriminated
Parsedunion reports success or errors. - Each field is checked and defaulted individually.
- A type predicate filters the tags array safely.
Keywords and builtins used here
ArrayConfigIssueNaNNumberRecordbooleanconstelsefalseforfunctionifinterfacekeyofnumberofreturnstringtruetype
The run, in numbers
- Lines
- 44
- Characters to type
- 1308
- Tokens
- 370
- Three-star pace
- 105 tpm
At the three-star pace of 105 tokens a minute, this run takes about 211 seconds.
Step 1 of 1 in Encore, step 23 of 23 in Types & annotations.