export function clamp(min: number, v: number, max: number): number { return Math.min(Math.max(min, v), max); } export function exhaustive(unexpected: never): never { throw err`Unexpected value ${unexpected}`; } export function fmt(lits: TemplateStringsArray, ...values: unknown[]): string { let out: unknown[] = []; for (let i = 0; ; i++) { out.push(lits[i]); if (i >= values.length) break; let v = values[i]; let type = typeof v; switch (type) { case "bigint": case "boolean": case "number": case "symbol": case "undefined": out.push(v); break; case "object": case "string": out.push(JSON.stringify(v)); break; case "function": out.push(`[function ${(v as Function).name}]`); break; default: let _: never = type; out.push(JSON.stringify(v)); } } return out.join(""); } class InterpolatedError extends Error { constructor( message: string, readonly values: unknown[], ) { super(message); } } export function err(lits: TemplateStringsArray, ...values: unknown[]): Error { return new InterpolatedError(fmt(lits, ...values), values); }