TypeDrop
2026-06-15 Challenge
2026-06-15
Medium
Typed Job Queue Scheduler with Priority & Retry
You're building the background job processing layer for a SaaS platform. Raw job definitions arrive as `unknown` from a REST API; your scheduler must validate them, enqueue them by priority, execute them with concurrency limits, and produce a strongly-typed execution report — with zero `any`.
Goals
- Implement `validateJob` to parse `unknown` API payloads into a discriminated-union `Job`, returning a typed `Result<Job, ValidationError>` for every failure case.
- Implement `enqueueByPriority` to stably sort jobs by priority tier (high → normal → low) without mutating the input array.
- Implement `runScheduler` to execute jobs with a sliding-window concurrency pool, per-job retry budgets, and correct `succeeded` / `exhausted` / `failed` status tracking.
- Implement `buildReport` as a pure single-pass `reduce` that tallies outcomes and returns a `SchedulerReport` whose `outcomes` field is the exact same array reference passed in.
challenge.ts
// Key types & main function signatures
export type Priority = "high" | "normal" | "low";
export type JobKind = "email" | "report" | "export";
export type Job =
| { id: string; kind: "email"; priority: Priority; retries: number; payload: EmailPayload }
| { id: string; kind: "report"; priority: Priority; retries: number; payload: ReportPayload }
| { id: string; kind: "export"; priority: Priority; retries: number; payload: ExportPayload };
export type Result<T, E> =
| { ok: true; value: T }
| { ok: false; error: E };
// Maps every JobKind to a strongly-typed handler — must be complete
export type HandlerRegistry = {
[K in JobKind]: (job: Extract<Job, { kind: K }>) => Promise<boolean>;
};
export function validateJob(raw: unknown): Result<Job, ValidationError>;
export function enqueueByPriority(jobs: Job[]): Job[];
export async function runScheduler(
jobs: Job[],
registry: HandlerRegistry,
config: SchedulerConfig // { concurrency: number; maxAttempts: number }
): Promise<SchedulerReport>;
export function buildReport(outcomes: JobOutcome[]): SchedulerReport;
Hints (click to reveal)
Hints
- For `HandlerRegistry`, the mapped type `[K in JobKind]: JobHandler<K>` forces you to handle every kind — lean on `Extract<Job, { kind: K }>` to get the right payload type per handler.
- A sliding-window concurrency pool can be built by maintaining a `Set` of in-flight Promises and `await`-ing the fastest one (via `Promise.race`) whenever the pool is full.
- In `validateJob`, narrow `raw` with `typeof` / `in` guards first, then use a `switch` on `kind` to validate the per-kind payload fields — the compiler will track the narrowed type in each branch.
Useful resources
Or clone locally
git clone -b challenge/2026-06-15 https://github.com/niltonheck/typedrop.git