TypeDrop

2026-06-29 Challenge

2026-06-29 Medium

Typed Feature Flag Engine with Targeting Rules & Rollout Segments

You're building the feature-flag evaluation engine for a SaaS platform. Raw flag configurations arrive as `unknown` from a remote config store; your engine must validate them into strongly-typed flag definitions, evaluate targeting rules against a typed user context, and return a fully-typed evaluation result — with zero `any`.

Goals

  • Complete the `FlagValue<T>` conditional type so each `FlagValueType` string literal maps to its corresponding TypeScript primitive.
  • Implement `validateFlagDefinition` to parse an `unknown` blob into a `FlagDefinition<FlagValueType>`, collecting all `ValidationError`s and returning a `Result<FlagDefinition<FlagValueType>, ValidationError[]>`.
  • Implement `evaluateRule` using an exhaustive type-narrowing `switch` on `rule.kind`, and `evaluateFlag` to walk the rule list and return the correct `EvalResult`.
  • Complete `FlagRegistryBuilder` so `.add()` widens the generic key union and `.build()` returns a correctly-typed `FlagRegistry<K>`.
challenge.ts
// Key types and main function signature preview

type FlagValueType = "boolean" | "string" | "number";

type FlagValue<T extends FlagValueType> =
  T extends "boolean" ? boolean :
  T extends "string"  ? string  :
  T extends "number"  ? number  : never;

type TargetingRule =
  | { kind: "exact";   attribute: string; value: string }
  | { kind: "set";     attribute: string; values: readonly string[] }
  | { kind: "rollout"; percentage: number };

interface FlagDefinition<T extends FlagValueType> {
  readonly id: string;
  readonly valueType: T;
  readonly rules: ReadonlyArray<{ rule: TargetingRule; value: FlagValue<T> }>;
  readonly defaultValue: FlagValue<T>;
}

// Evaluate a single flag for a user — returns a typed discriminated result
declare function evaluateFlag<K extends string>(
  registry: FlagRegistry<K>,
  flagId: string,
  ctx: UserContext
): EvalResult<FlagValueType>;
Hints (click to reveal)

Hints

  • For `FlagValue<T>`, chain `T extends 'boolean' ? boolean : T extends 'string' ? string : ...` — conditional types distribute over each branch.
  • In `FlagRegistryBuilder.add()`, the return type `FlagRegistryBuilder<K | typeof flag.id>` is already provided — store flags in a private `Map<string, FlagDefinition<FlagValueType>>` and cast the builder's `this` on return.
  • In `evaluateRule`'s `switch`, add a default branch that assigns `rule` to `never` (the exhaustiveness sentinel) — TypeScript will error if you forget a case.

Or clone locally

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