TypeDrop

2026-03-10 Challenge

2026-03-10 Easy

Typed Contact Book Merger

You're building the import feature for a personal CRM app. Users can sync contacts from multiple sources (phone, email, LinkedIn), and your job is to validate raw unknown input, merge duplicate contacts by email, and produce a clean, strongly-typed contact list — all with zero `any`.

Goals

  • Implement `isContactSource` as a type guard that narrows `unknown` to the `ContactSource` union.
  • Implement `parseRawContact` to validate every field of an unknown record and return a typed `ParseResult<Contact>` with normalised values.
  • Implement `mergeContacts` to run the parser over all raw records, collect failures, and merge duplicate contacts by email using a `Map` — keeping the first non-null phone and unioning sources.
challenge.ts
export type ContactSource = "phone" | "email" | "linkedin";

export interface Contact {
  email: string;
  name: string;
  phone: string | null;
  sources: ContactSource[];
}

export type ParseResult<T> =
  | { ok: true; value: T }
  | { ok: false; error: string };

// Narrows an unknown value to a ContactSource literal
export function isContactSource(value: unknown): value is ContactSource { /* TODO */ }

// Validates & normalises a single unknown record
export function parseRawContact(raw: unknown): ParseResult<Contact> { /* TODO */ }

// Merges an array of unknown records, deduplicating by email
export function mergeContacts(
  rawRecords: unknown[]
): { contacts: Contact[]; failures: Array<{ ok: false; error: string }> } { /* TODO */ }
Hints (click to reveal)

Hints

  • A type guard function returns `value is SomeType` — use `===` checks against each literal string to narrow `ContactSource`.
  • In `parseRawContact`, check `typeof raw === 'object' && raw !== null && !Array.isArray(raw)` first, then use `'email' in raw` before accessing any property.
  • Use `new Map<string, Contact>()` in `mergeContacts` and `new Set(existing.sources)` to deduplicate sources when merging a duplicate email.

Or clone locally

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