Template literal types require TS 4.1+
Use template literal types to create a compile-time-checked route builder.
1 // Extract params from route string 2 type ExtractRouteParams<T extends string> = 3 string extends T ? Record<string, string> : 4 T extends `${infer _Start}:${infer Param}/${infer Rest}` ? 5 { [k in Param | keyof ExtractRouteParams<`/${Rest}`>]: string } : 6 T extends `${infer _Start}:${infer Param}` ? 7 { [k in Param]: string } : 8 Record<string, never>; 9 10 // HTTP method map with capitalized type names 11 type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'; 12 type RouteHandler<Path extends string> = ( 13 params: ExtractRouteParams<Path>, 14 query: Record<string, string> 15 ) => Promise<unknown>; 16 17 // Type-safe router 18 class Router { 19 private routes = new Map<string, RouteHandler<string>>(); 20 21 on<Path extends string>(method: HttpMethod, path: Path, handler: RouteHandler<Path>) { 22 this.routes.set(`${method}:${path}`, handler as RouteHandler<string>); 23 return this; 24 } 25 } 26 27 // CSS-in-JS value type 28 type CSSUnit = 'px' | 'em' | 'rem' | '%' | 'vw' | 'vh'; 29 type CSSSize = `${number}${CSSUnit}`; 30 31 // Getter method names 32 type Getters<T> = { 33 [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] 34 }; 35 36 interface Config { host: string; port: number; debug: boolean; } 37 type ConfigGetters = Getters<Config>; 38 // { getHost: () => string; getPort: () => number; getDebug: () => boolean } 39 40 // Event system 41 type EventMap = { 42 'user:login': { userId: string; timestamp: Date }; 43 'user:logout': { userId: string }; 44 'order:placed': { orderId: string; total: number }; 45 }; 46 47 class TypedEventBus { 48 private handlers = new Map<string, Function[]>(); 49 50 on<K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void) { 51 if (!this.handlers.has(event)) this.handlers.set(event, []); 52 this.handlers.get(event)!.push(handler); 53 } 54 55 emit<K extends keyof EventMap>(event: K, data: EventMap[K]) { 56 this.handlers.get(event)?.forEach(h => h(data)); 57 } 58 } 59 60 const bus = new TypedEventBus(); 61 bus.on('user:login', ({ userId, timestamp }) => { 62 console.log(`User ${userId} logged in at ${timestamp}`); 63 }); 64 bus.emit('user:login', { userId: 'u-1', timestamp: new Date() });
router.get("/users/:id") receives typed params.id; wrong param name is a TS errorSign in to share your feedback and join the discussion.