TypeDrop
2026-05-02 Challenge
2026-05-02
Easy
Typed Event RSVP Aggregator
You're building the guest-management layer for an event-planning app. Raw RSVP payloads arrive as `unknown` from a public form endpoint; your aggregator must validate them, tally attendance per event, and return a strongly-typed summary — with zero `any`.
Goals
- Implement `validateRsvp` to narrow `unknown` payloads into typed `Rsvp` values, returning the correct `RsvpValidationError` code for each failure mode.
- Implement `buildEventSummary` to compute `attendingCount`, `declinedCount`, `maybeCount`, and `totalAttending` (primary guest + plusOnes) from a list of validated RSVPs.
- Implement `aggregateRsvps` to validate all payloads, group valid RSVPs by `eventId`, produce one `EventSummary` per event, and collect all failures — never throwing.
- Use only `readonly` properties, discriminated-union narrowing, and utility types — no `any`, `as`, or `!`.
challenge.ts
/** Possible responses a guest can submit. */
type RsvpStatus = "attending" | "declined" | "maybe";
/** A validated RSVP from a single guest. */
interface Rsvp {
readonly guestName: string;
readonly eventId: string;
readonly status: RsvpStatus;
readonly plusOnes: number; // ≥ 0; defaults to 0 if absent
}
type RsvpValidationError =
| "MISSING_GUEST_NAME" | "MISSING_EVENT_ID"
| "INVALID_STATUS" | "INVALID_PLUS_ONES"
| "NOT_AN_OBJECT";
type Result<T, E extends string = string> =
| { readonly ok: true; readonly value: T }
| { readonly ok: false; readonly error: E };
// ── Functions you must implement ──────────────────────────────
declare function validateRsvp(payload: unknown): Result<Rsvp, RsvpValidationError>;
declare function buildEventSummary(eventId: string, rsvps: readonly Rsvp[]): EventSummary;
declare function aggregateRsvps(payloads: readonly unknown[]): AggregationReport;
Hints (click to reveal)
Hints
- Use `typeof payload === 'object' && payload !== null` as your first guard before narrowing individual fields with `'guestName' in payload`.
- A small helper like `const STATUSES: readonly RsvpStatus[] = [...]` plus `.includes()` lets you validate the status union without casting.
- Build a `Record<string, Rsvp[]>` accumulator inside `aggregateRsvps` using a plain `for` loop or `Array.reduce` — then call `buildEventSummary` on each bucket.
Useful resources
Or clone locally
git clone -b challenge/2026-05-02 https://github.com/niltonheck/typedrop.git