TypeDrop

2026-04-10 Challenge

2026-04-10 Easy

Typed Task Queue Scheduler

You're building the background job runner for a productivity app. Raw task definitions arrive from a local database as unknown blobs; your scheduler must validate them, sort them by priority and deadline, and return a fully typed execution plan — with zero `any`.

Goals

  • Define PRIORITY_WEIGHTS using the `satisfies` operator so every Priority key is present and all values are numbers.
  • Implement `validateTask` to safely narrow `unknown` → `Result<Task, ValidationError>`, returning the first validation error found.
  • Implement `computeRank` to produce a branded `ScheduleRank` using the priority weight formula.
  • Implement `scheduleTasks` to validate, rank, sort, re-number, and aggregate all valid tasks into a typed `ExecutionPlan`.
challenge.ts

// Key types and main function signature

export type Priority = "low" | "medium" | "high";

export interface Task {
  id: string;
  title: string;
  priority: Priority;
  deadline: string;         // ISO-8601 e.g. "2026-04-15"
  estimatedMinutes: number; // positive integer
}

export type Result<T, E> =
  | { ok: true;  value: T }
  | { ok: false; error: E };

export type ValidationError =
  | { kind: "missing_field";  field: string }
  | { kind: "invalid_type";   field: string; expected: string }
  | { kind: "invalid_value";  field: string; message: string };

export type ScheduleRank = number & { readonly __brand: "ScheduleRank" };

export interface ExecutionPlan {
  scheduled: { task: Task; rank: ScheduleRank }[];
  totalEstimatedMinutes: number;
  generatedAt: string;
}

// Entry point — validate, rank, sort, and plan
export function scheduleTasks(
  rawTasks: unknown[],
  now?: string
): ExecutionPlan { /* TODO */ }
Hints (click to reveal)

Hints

  • For `validateTask`, check `typeof raw === 'object' && raw !== null` first, then use `'field' in raw` guards to safely access each property.
  • The `satisfies` operator lets you keep a concrete literal type while still checking the shape — put it right after the object literal: `{ ... } satisfies Record<Priority, number>`.
  • To brand a plain number as `ScheduleRank`, the one allowed cast site is the return of `computeRank`: `return computedNumber as ScheduleRank`.

Or clone locally

git clone -b challenge/2026-04-10 https://github.com/niltonheck/typedrop.git