TypeDrop

2026-04-04 Challenge

2026-04-04 Easy

Typed User Profile Merger

You're building the account-settings feature for a SaaS app. When a user submits a partial profile update, your engine must validate the raw incoming fields, deep-merge them onto the existing profile, and return a fully typed `Result<T, E>` — with zero `any`.

Goals

  • Define `ProfileUpdate` as a mapped/utility type that makes all user-editable `UserProfile` fields optional while excluding `id` and `updatedAt`.
  • Define `MergeError` as a discriminated union with three `kind` variants, each carrying appropriate detail fields.
  • Implement the three type-predicate validators (`validateEmail`, `validateHttpsUrl`, `validatePlatform`) with correct narrowing signatures.
  • Implement `mergeProfile` to validate all incoming fields in order, return typed `MergeError` on the first failure, and return a new immutable `UserProfile` with a refreshed `updatedAt` on success.
challenge.ts
/** Supported social-link platforms. */
type Platform = "github" | "twitter" | "linkedin";

interface SocialLink { platform: Platform; url: string; }

interface UserProfile {
  id: string; displayName: string; email: string;
  bio: string; avatarUrl: string; socials: SocialLink[];
  updatedAt: string; // system-managed
}

// All user-editable fields are optional; id & updatedAt are excluded
type ProfileUpdate = Omit<Partial<UserProfile>, "id" | "updatedAt">;

type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };

type MergeError =
  | { kind: "INVALID_EMAIL";    provided: string }
  | { kind: "INVALID_URL";      field: string; provided: string }
  | { kind: "UNKNOWN_PLATFORM"; provided: string };

declare function mergeProfile(
  existing: UserProfile,
  update: ProfileUpdate
): Result<UserProfile, MergeError>;
Hints (click to reveal)

Hints

  • For `ProfileUpdate`, you only need two built-in utility types composed in a single expression — think about which one makes fields optional and which one removes fields.
  • A type predicate has the return type `value is SomeType`; your `validatePlatform` predicate should narrow `string` all the way down to the `Platform` union.
  • In `mergeProfile`, validate fields in order and `return` early on the first error — you never need a nested `if/else` tree.

Or clone locally

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