TypeDrop

2026-04-14 Challenge

2026-04-14 Medium

Typed Feature Flag Evaluator

You're building the feature-flag evaluation engine for a product experimentation platform. Raw flag configurations arrive from a remote config service as unknown JSON; your engine must validate them, evaluate each flag against a typed user context, and return a strongly-typed rollout report — with zero `any`.

Goals

  • Implement `isAudience`, `isOperator`, and `isVariantKind` as proper type-guard functions that narrow `unknown` to their respective union types.
  • Implement `parseFlagVariant`, `parseTargetingRule`, and `parseFeatureFlag` to safely validate raw `unknown` JSON into typed domain objects, throwing descriptive errors on bad input.
  • Implement `evaluateRule` to correctly evaluate all six operators (`eq`, `neq`, `in`, `not_in`, `gte`, `lte`) against a `UserContext`.
  • Implement `evaluateFlags` to orchestrate validation, audience gating, rule matching, and aggregate the results into a fully typed `EvaluationReport` with accurate summary counts.
challenge.ts
// Core domain types + main function signature

type Audience = "internal" | "beta" | "public";
type Operator = "eq" | "neq" | "in" | "not_in" | "gte" | "lte";

interface UserContext {
  userId: string; email: string; plan: string;
  country: string; accountAgeDays: number; audience: Audience;
}

type FlagVariant =
  | { kind: "boolean"; enabled: boolean }
  | { kind: "string";  variant: string  }
  | { kind: "number";  variant: number  };

type FlagResult =
  | { status: "matched"; flagId: string; rule: TargetingRule; variant: FlagVariant }
  | { status: "default"; flagId: string; variant: FlagVariant }
  | { status: "invalid"; flagId: string; error: string };

interface EvaluationReport {
  userId: string; evaluatedAt: string;
  results: FlagResult[];
  summary: { total: number; matched: number; defaulted: number; invalid: number };
}

// Entry point — validate raw blobs, evaluate rules, return typed report
function evaluateFlags(rawFlags: unknown[], user: UserContext): EvaluationReport { ... }
Hints (click to reveal)

Hints

  • Use an `as const` array (e.g. `const VALID_AUDIENCES = [...] as const`) combined with `.includes()` to implement membership-check type guards without `any` — you may need a small cast on the `includes` call since TS widens the array type; consider `(arr as readonly unknown[]).includes(value)`.
  • In `parseFlagVariant`, switch on `kind` after validating it with `isVariantKind` — TypeScript will narrow the discriminated union branch for you inside each `case`.
  • In `evaluateFlags`, the audience gate rule is: a flag is eligible if `flag.audience === 'public'` OR `flag.audience === user.audience`; anything else resolves to `status: 'default'` before rules are even checked.

Or clone locally

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