TypeDrop
2026-08-05 Challenge
2026-08-05
Easy
Typed Safe JSON Parser & Result Unwrapper
You're building a lightweight data-ingestion utility for a dashboard that consumes JSON payloads from third-party webhooks. Payloads can be malformed, partially missing required fields, or entirely the wrong shape — so every parse must return a typed `Result<T, ParseError>` instead of throwing, and callers must exhaustively handle both branches.
Goals
- Implement the `ok` and `err` smart constructors and use them consistently so callers never construct `Result` objects by hand.
- Implement `getString` and `getNumber` using `typeof` narrowing on `unknown` — no type assertions allowed.
- Implement `parseOrderPayload` and `parseRefundPayload` with fail-fast error propagation, returning the first `ParseError` encountered.
- Make `describeError` exhaustively handle every `ParseError` variant so TypeScript raises a compile error if a new variant is ever added without updating the function.
challenge.ts
// Key types — understand these before writing any logic
export type Ok<T> = { readonly kind: "ok"; readonly value: T };
export type Err<E> = { readonly kind: "err"; readonly error: E };
export type Result<T, E> = Ok<T> | Err<E>;
export type ParseError =
| { readonly kind: "invalid_json"; readonly raw: string }
| { readonly kind: "missing_field"; readonly field: string }
| { readonly kind: "wrong_type"; readonly field: string; readonly expected: string };
export interface OrderPayload {
readonly orderId: string;
readonly customerId: string;
readonly totalCents: number;
readonly placedAt: string;
}
// Core functions you must implement:
export function safeParseJSON(raw: string): Result<unknown, ParseError> { ... }
export function getString(obj: unknown, field: string): Result<string, ParseError> { ... }
export function parseOrderPayload(raw: string): Result<OrderPayload, ParseError> { ... }
export function unwrapOr<T, E>(result: Result<T, E>, fallback: T): T { ... }
export function describeError(error: ParseError): string { ... }
Hints (click to reveal)
Hints
- For `getString`/`getNumber`, first check `typeof obj === 'object' && obj !== null` before indexing — TypeScript will narrow `obj` to `object`, then use `field in obj` to confirm the key exists.
- For exhaustiveness in `describeError`, add a `default` branch that assigns `error` to a variable typed `never` — the compiler will error if any variant slips through.
- In `parseOrderPayload`, chain your field extractions imperatively: call `getString(parsed, 'orderId')`, check `.kind === 'err'` and early-return, then move to the next field — this is simpler than monadic `flatMap` for a first pass.
Useful resources
Or clone locally
git clone -b challenge/2026-08-05 https://github.com/niltonheck/typedrop.git