TypeDrop

2026-07-04 Challenge

2026-07-04 Easy

Typed Event RSVP Aggregator

You're building the RSVP processing module for an event management platform. Raw RSVP submissions arrive as `unknown` from a webhook; your module must validate them into strongly-typed `Rsvp` records, tally responses by status, and return a fully-typed `RsvpSummary` — with zero `any`.

Goals

  • Define the `Rsvp`, `Result<T>`, and `RsvpSummary` types with correct field types and constraints.
  • Implement `parseRsvp` to validate an `unknown` payload into a `Result<Rsvp>`, returning a failure Result for any invalid field.
  • Implement `aggregateRsvps` to silently skip invalid or mismatched payloads and return an `RsvpSummary` with a complete `Record<RsvpStatus, number>` — defaulting all statuses to 0.
  • Use `Record<RsvpStatus, number>` so the compiler enforces that every status is always present in the counts map.
challenge.ts

/** The three possible RSVP responses an attendee can give. */
type RsvpStatus = "attending" | "declined" | "maybe";

/** A validated RSVP record. */
type Rsvp = {
  id: string;
  eventId: string;
  name: string;
  email: string;
  status: RsvpStatus;
  guests: number;
};

/** Success-or-failure wrapper — no exceptions thrown. */
type Result<T> =
  | { ok: true;  value: T }
  | { ok: false; error: string };

/** Per-status tally for a single event. */
type RsvpSummary = {
  eventId: string;
  counts: Record<RsvpStatus, number>;
  totalGuests: number;
};

// Validate a raw webhook payload into a typed Rsvp
declare function parseRsvp(raw: unknown): Result<Rsvp>;

// Aggregate an array of raw payloads for one event
declare function aggregateRsvps(eventId: string, raws: unknown[]): RsvpSummary;
Hints (click to reveal)

Hints

  • To check that a string is a valid `RsvpStatus`, create a constant array `const STATUSES = ['attending', 'declined', 'maybe'] as const` and use `Array.prototype.includes` — you may need a small helper to satisfy the type checker.
  • Build the initial `counts` object using `Object.fromEntries` over the known statuses so TypeScript infers `Record<RsvpStatus, number>` without casting.
  • When narrowing `unknown`, check `typeof raw === 'object' && raw !== null` first, then use the `in` operator to verify each key exists before accessing it.

Or clone locally

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