TypeDrop
2026-06-27 Challenge
2026-06-27
Medium
Typed Job Queue with Priority Scheduling & Retry Policies
You're building the background job processing engine for a SaaS platform. Raw job submissions arrive as `unknown` from an HTTP API; your queue must validate them into strongly-typed job descriptors, schedule them by priority, execute them through a typed handler registry, apply per-job-kind retry policies, and return a fully-typed execution receipt — with zero `any`.
Goals
- Implement `validateJobDescriptor` to safely narrow `unknown` into a fully-typed `JobDescriptor`, returning `null` for any invalid input without ever throwing.
- Implement `scheduleJobs` to sort descriptors by priority ascending, breaking ties by `enqueuedAt` ascending, without mutating the input array.
- Implement `executeJob` to dispatch to the correct typed handler via `HandlerRegistry`, retrying up to `maxAttempts` times with `delayMs` waits, and returning a typed `ExecutionResult`.
- Implement `runQueue` to run the full validate → schedule → execute pipeline, processing jobs sequentially and collecting all `ExecutionResult` values.
challenge.ts
// Core types & main function signatures at a glance
type Priority = 1 | 2 | 3;
type Job = SendEmailJob | ResizeImageJob | GenerateReportJob;
type JobKind = Job["kind"]; // "send_email" | "resize_image" | "generate_report"
// Extracts the payload type for a given kind — you must complete this:
type PayloadOf<K extends JobKind> = Extract<Job, { kind: K }>["payload"];
// Every kind must have a handler — you must complete this mapped type:
type HandlerRegistry = { [K in JobKind]: HandlerFn<K> };
type ExecutionResult = SuccessResult | FailureResult;
// Validate raw unknown → typed descriptor (or null):
function validateJobDescriptor(raw: unknown): JobDescriptor | null { ... }
// Sort descriptors: priority ASC, then enqueuedAt ASC:
function scheduleJobs(jobs: ReadonlyArray<JobDescriptor>): JobDescriptor[] { ... }
// Run one job with typed handler + retry policy:
async function executeJob(
descriptor: JobDescriptor,
handlers: HandlerRegistry,
policies: RetryPolicyRegistry
): Promise<ExecutionResult> { ... }
// Full pipeline: validate → schedule → execute sequentially:
async function runQueue(
rawJobs: unknown[],
handlers: HandlerRegistry,
policies: RetryPolicyRegistry
): Promise<ExecutionResult[]> { ... }
Hints (click to reveal)
Hints
- For `validateJobDescriptor`, switch on `kind` first to narrow which payload shape to check — TypeScript will understand the narrowing inside each `case` branch.
- For `executeJob`, a small helper like `function dispatch<K extends JobKind>(k: K, h: HandlerRegistry, p: PayloadOf<K>): Promise<string>` lets you call `h[k](p)` without `any` — the mapped type makes the index signature safe.
- The `RetryPolicyRegistry` and `HandlerRegistry` types are mapped types over `JobKind` — write `[K in JobKind]: ...` to enforce that every kind has an entry at compile time.
Useful resources
Or clone locally
git clone -b challenge/2026-06-27 https://github.com/niltonheck/typedrop.git