type Path = (string | number)[]; export function optional(obj: any, fn: (obj: any) => T): T | undefined { return obj !== undefined ? fn(obj) : undefined; } export function iface(input: any, path: Path, fn: (obj: any) => T): T { if (typeof input !== "object") { throw new Error(`expected object at ${path}, got ${typeof input}`); } return fn(input); } export function array( input: any, path: Path, fn: (idx: number, obj: any) => T, ): T[] { if (!Array.isArray(input)) { throw new Error(`expected array, got ${typeof input}`); } return input.map((idx, val) => fn(idx, val)); } export function string(input: any, path: Path): string { if (typeof input !== "string") { throw new Error( `expected string at ${path.join(".")}, got ${typeof input}`, ); } return input as string; } export function number(input: any, path: Path): number { if (typeof input === "string") { // Parse the input as a number const num = Number(input); if (Number.isNaN(num)) { throw new Error( `expected number at ${path.join(".")}, got ${typeof input}`, ); } return num; } else if (typeof input !== "number") { throw new Error( `expected number at ${path.join(".")}, got ${typeof input}`, ); } return input as number; }