prefer-discriminated-union
Preserve producer-proven tag/payload and Boolean state relationships in return types.
Why
A flat object with optional branch data loses the relationship between a tag and its payload. Callers cannot narrow to the required fields, and the type permits incomplete or contradictory combinations.
Fix
If the variants are mutually exclusive, represent each with a literal tag and its required payload. For predicates over one current state, consider returning that authoritative state instead of storing multiple derived flags. Preserve every valid state, independent dimension, shared field, payload value type and nullability. For a producer returning a subset of a broader wire contract, give the producer a precise domain return type after validation.
Examples
type Phase = "ready" | "waiting" | "unknown";declare function snapshot(): Phase;function state(): { ready: boolean; waiting: boolean } { const phase = snapshot(); return { ready: phase === "ready", waiting: phase === "waiting" };}type Phase = "ready" | "waiting" | "reconnecting" | "unknown";declare function snapshot(): Phase;function state(timeout: boolean): { phase: Phase; timeout: boolean } { const phase = snapshot(); return { phase, timeout };}function job(done: boolean): { phase: "queued" | "complete"; ticket?: string } { return done ? { phase: "complete" } : { phase: "queued", ticket: "t" };}function job( done: boolean,): { phase: "queued"; ticket: string } | { phase: "complete" } { return done ? { phase: "complete" } : { phase: "queued", ticket: "t" };}function result(ok: boolean): { ok: boolean; data?: string; error?: string } { return ok ? { ok: true, data: "value" } : { ok: false, error: "failed" };}type Result = { ok: true; data: string } | { ok: false; error: string };function job(command: string): { phase: "queued" | "complete"; ticket?: string } { switch (command) { case "start": return { phase: "queued", ticket: "t" }; default: return { phase: "complete" }; }}function job( command: string,): { phase: "queued"; ticket: string } | { phase: "complete" } { switch (command) { case "start": return { phase: "queued", ticket: "t" }; default: return { phase: "complete" }; }}function failure( retry: boolean, saved: boolean,): { retriable: boolean; persisted: boolean } { return saved ? { retriable: false, persisted: true } : retry ? { retriable: true, persisted: false } : { retriable: false, persisted: false };}function observations(a: boolean, b: boolean): { started: boolean; reported: boolean } { return a ? b ? { started: true, reported: true } : { started: true, reported: false } : b ? { started: false, reported: true } : { started: false, reported: false };}