TypeDrop
2026-03-13 Challenge
2026-03-13
Hard
Typed Concurrent Task Scheduler with Priority Queues
You're building the background job engine for a data-pipeline platform. Tasks arrive with a priority level and resource tags, must be executed with a concurrency cap per resource group, and every outcome — success, failure, or cancellation — must be surfaced through a fully typed Result hierarchy with zero `any`.
Goals
- Define branded TaskId, Priority, TaskError, and TaskResult<T> types with zero `any` and no type casts.
- Implement createScheduler() so tasks are dispatched in priority order while respecting both per-group and global concurrency caps.
- Handle timeout races, runtime rejections, and queue-overflow cancellations — each surfaced as the correct TaskResult variant.
- Derive SchedulerStats.totals via a mapped type over GroupCounts keys so the shape stays in sync automatically.
challenge.ts
// Key types and main entry-point — enough to understand the challenge
declare const __taskIdBrand: unique symbol;
export type TaskId = string & { readonly [__taskIdBrand]: true };
export type Priority = "low" | "normal" | "high" | "critical";
export type TaskError =
| { kind: "timeout"; durationMs: number }
| { kind: "runtime"; message: string; cause?: unknown }
| { kind: "overflow"; queueSize: number };
export type TaskResult<T> =
| { status: "fulfilled"; id: TaskId; value: T }
| { status: "rejected"; id: TaskId; error: TaskError }
| { status: "cancelled"; id: TaskId; reason: string };
export interface TaskDefinition<T> {
id: TaskId;
priority: Priority;
resourceGroup: string;
run: () => Promise<T>;
timeoutMs?: number;
}
export interface Scheduler {
submit<T>(task: TaskDefinition<T>): Promise<TaskResult<T>>;
cancel(id: TaskId): boolean;
stats(): SchedulerStats;
}
export function createScheduler(config: SchedulerConfig): Scheduler { /* TODO */ }
Hints (click to reveal)
Hints
- A priority queue can be a plain array you re-sort on insert — use the PRIORITY_ORDER weight map to compare entries.
- To race a task against its timeout, wrap both in Promise.race: one resolves with the value, the other rejects after timeoutMs ms.
- Store each queued task alongside its resolve function so that cancel() and overflow can settle the promise from outside the run loop.
Useful resources
Or clone locally
git clone -b challenge/2026-03-13 https://github.com/niltonheck/typedrop.git