// ============ An example using dependent/constructed types to build route handlers ============ // This is a more advanced example that prevents a hypothetical web route definition library // from accepting handler functions whose parameters do not match the types of the provided parameters. // This is a simplification of real-world routing implementation I built in @extollo/lib: // https://code.garrettmills.dev/extollo/lib/src/branch/master/src/http/routing/Route.ts // https://code.garrettmills.dev/extollo/lib/src/branch/master/src/util/support/types.ts // First, we'll define some helper types for constructing/destructing variadic tuple types: /** Takes a tuple and a type and produces a new tuple suffixed w/ the type. */ type AppendTypeArray = [...Arr, Item] /** Converts a tuple of types and formats it as a function signature. */ type TypeArraySignature = (...params: Arr) => Return type ExampleTypeArray = AppendTypeArray<['test', 3.14], true> // => ['test', 3.14, true] type ExampleTypeSignature = TypeArraySignature // => ('test', 3.14, true) => void // Now, some helper types used by the Route class: // eslint-disable-next-line @typescript-eslint/no-empty-interface interface Request {} // Stub type for the HTTP request // eslint-disable-next-line @typescript-eslint/no-empty-interface interface HandledRoute {} // Prevents adding parameters to handled routes /** Type of functions that provide parameters to the request handler. */ type ParameterProvider = (request: Request) => T /** * Stub route definition implementation. The type of the handler function * accepted by `handleWith(...)` depends on the parameter middleware added * via `withParameter(...)`. */ export class Route { /** Start a net GET route. */ public static get(endpoint: string): Route<[]> { return new Route(endpoint) } constructor( public readonly endpoint: string, ) {} /** The collected parameter middleware. */ protected parameters: ParameterProvider[] = [] /** Register a new middleware parameter. */ public withParameter(provider: ParameterProvider): Route> { this.parameters.push(provider) return this as unknown as Route> } /** Register an appropriately-typed handler for this route. */ public handleWith(handler: TypeArraySignature): HandledRoute { return this } } Route.get('/no-param') .handleWith(() => 'hello!') Route.get('/one-param') .withParameter(() => 3.14) .handleWith((pi: number) => `Pi is ${pi}!`) Route.get('/two-params') .withParameter(() => 'Hello, ') .withParameter(() => 'John Doe') .handleWith((greeting: string, name: string) => `${greeting}${name}`) /* Route.get('/invalid-params') .withParameter(() => 3.14) .handleWith((pi: number, greeting: string) => `${pi}${greeting}`)*/ /* Route.get('/invalid-method-call') .handleWith(() => 'Hello!') .withParameter(() => 3.14)*/