TypeDrop
2026-08-29 Challenge
2026-08-29
Hard
Typed Middleware Pipeline with Inferred Context Accumulation
You're building the request-handling core for an internal HTTP gateway. Middleware layers run sequentially, each one reading from — and optionally enriching — a typed context object. The compiler must guarantee that a middleware can only access fields that prior layers have already attached, and that the final handler receives the exact accumulated context type — no more, no less.
Goals
- Implement `createPipeline` as a type-safe fluent builder that threads an ever-growing context type through sequential middleware layers.
- Implement `run` to execute layers in order, short-circuiting immediately on the first `{ ok: false }` result.
- Implement `composeMiddleware` to fuse two compatible middleware into one, propagating failures from the first before invoking the second.
- Implement the `ExtractCtx` and `ContextDiff` utility types using `infer` and mapped-type key remapping respectively.
challenge.ts
// Key types — understand these before implementing:
type BaseCtx = Record<string, unknown>;
type MiddlewareResult<TOut extends BaseCtx> =
| { ok: true; ctx: TOut }
| { ok: false; error: MiddlewareError };
type Middleware<TIn extends BaseCtx, TOut extends TIn> = {
name: string;
handler: (ctx: TIn) => Promise<MiddlewareResult<TOut>>;
};
// The builder interface — TCtx grows with each `.use()` call:
interface Pipeline<TCtx extends BaseCtx> {
use<TOut extends TCtx>(mw: Middleware<TCtx, TOut>): Pipeline<TOut>;
run(initialCtx: TCtx): Promise<MiddlewareResult<TCtx>>;
}
// Entry point — start with your seed context shape:
declare function createPipeline<T extends BaseCtx>(): Pipeline<T>;
Hints (click to reveal)
Hints
- The `Pipeline` interface's `use` method must return `Pipeline<TOut>` — store handlers in a plain array internally and cast the return type of `use` at the boundary (the one place a cast is acceptable in a builder pattern).
- For `ContextDiff`, use a mapped type with an `as` clause: `[K in keyof TAfter as K extends keyof TBefore ? never : K]` to filter out pre-existing keys.
- In `run`, iterate the stored handlers with a `for...of` loop and an `await` inside — if any result has `ok: false`, return it immediately; only return success after the loop completes.
Useful resources
Or clone locally
git clone -b challenge/2026-08-29 https://github.com/niltonheck/typedrop.git