TypeDrop
2026-06-02 Challenge
2026-06-02
Easy
Typed Contact Book Grouper
You're building the display layer for a mobile contact book app. Raw contact entries arrive as `unknown` from a device sync API; your engine must validate them, normalize their data, and group them into a strongly-typed alphabetical index — with zero `any`.
Goals
- Implement `isPlainObject` as a type predicate that distinguishes plain objects from arrays, primitives, and null.
- Implement `validateContact` to validate each field according to its rules and normalize `fullName` (trim) and `phone` (blank string → null).
- Implement `resolveAlphaKey` to map a contact's first character to the correct `AlphaKey` letter or the `"#"` fallback.
- Implement `groupContacts` to process all raw entries, collect typed validation errors, build the alphabetical `ContactIndex`, and sort each bucket case-insensitively by `fullName`.
challenge.ts
export type ContactCategory = "personal" | "work";
export interface Contact {
id: string;
fullName: string; // trimmed, non-empty
email: string; // must contain "@"
phone: string | null;
category: ContactCategory;
}
export type AlphaKey = Uppercase<
"a"|"b"|"c"|"d"|"e"|"f"|"g"|"h"|"i"|"j"|"k"|"l"|"m"|
"n"|"o"|"p"|"q"|"r"|"s"|"t"|"u"|"v"|"w"|"x"|"y"|"z"
> | "#";
export type ContactIndex = Partial<Record<AlphaKey, Contact[]>>;
export type GroupResult = { index: ContactIndex; errors: ValidationError[] };
// Main entry point — validate, normalize, and group raw contacts
export function groupContacts(rawEntries: unknown[]): GroupResult { ... }
Hints (click to reveal)
Hints
- A type predicate (`value is Record<string, unknown>`) lets the compiler narrow `unknown` inside every `if` branch that calls your guard — combine `typeof`, `!== null`, and `!Array.isArray` checks.
- The `AlphaKey` union is exhaustive: use `String.prototype.toUpperCase()` and check whether the result is a single A–Z letter (e.g. `/^[A-Z]$/.test(...)`) before falling back to `"#"`.
- `Partial<Record<AlphaKey, Contact[]>>` means each bucket may be `undefined` — initialise a bucket with an empty array the first time you encounter a new key, then push and sort.
Useful resources
Or clone locally
git clone -b challenge/2026-06-02 https://github.com/niltonheck/typedrop.git