Assertion functions require TS 3.7+
Create typed assertion functions for runtime validation with type narrowing.
1 // Core assert — truthy check 2 function assert(condition: unknown, message?: string): asserts condition { 3 if (!condition) { 4 throw new Error(message ?? `Assertion failed`); 5 } 6 } 7 8 // Type-specific assertions 9 function assertIsString(val: unknown, field = 'value'): asserts val is string { 10 if (typeof val !== 'string') { 11 throw new TypeError(`${field}: expected string, got ${typeof val}`); 12 } 13 } 14 15 function assertIsNumber(val: unknown, field = 'value'): asserts val is number { 16 if (typeof val !== 'number' || isNaN(val)) { 17 throw new TypeError(`${field}: expected number, got ${typeof val}`); 18 } 19 } 20 21 function assertIsDefined<T>(val: T, field = 'value'): asserts val is NonNullable<T> { 22 if (val === undefined || val === null) { 23 throw new Error(`${field}: expected defined value, got ${val}`); 24 } 25 } 26 27 function assertIsArray<T>(val: unknown): asserts val is T[] { 28 if (!Array.isArray(val)) { 29 throw new TypeError(`Expected array, got ${typeof val}`); 30 } 31 } 32 33 // assertNever — exhaustive discriminated unions 34 function assertNever(x: never, message = 'Unhandled case'): never { 35 throw new Error(`${message}: ${JSON.stringify(x)}`); 36 } 37 38 // Usage examples 39 function processConfig(raw: unknown) { 40 assert(typeof raw === 'object' && raw !== null, 'Config must be an object'); 41 // raw: object here 42 43 const config = raw as Record<string, unknown>; 44 assertIsString(config.apiKey, 'apiKey'); 45 assertIsNumber(config.timeout, 'timeout'); 46 // Now safely typed: 47 console.log(config.apiKey.toUpperCase()); // string methods safe 48 console.log(config.timeout.toFixed(0)); // number methods safe 49 return { apiKey: config.apiKey, timeout: config.timeout }; 50 } 51 52 // Test valid config 53 try { 54 const cfg = processConfig({ apiKey: 'abc-123', timeout: 5000 }); 55 console.log('Config valid:', cfg); 56 } catch (e) { console.error(e); } 57 58 // Test invalid config 59 try { 60 processConfig({ apiKey: 42, timeout: 'long' }); 61 } catch (e) { console.error('Expected error:', (e as Error).message); } 62 63 // Exhaustive switch 64 type Direction = 'north' | 'south' | 'east' | 'west'; 65 function describeDir(dir: Direction): string { 66 switch (dir) { 67 case 'north': return 'Going up'; 68 case 'south': return 'Going down'; 69 case 'east': return 'Going right'; 70 case 'west': return 'Going left'; 71 default: return assertNever(dir); 72 } 73 }
After assertIsString(x), x.toUpperCase() compiles without casts
Sign in to share your feedback and join the discussion.