Your Sunday, upgraded. Free live AI class · 5 PM ISTReserve your seat
PROGRAMMING LANGUAGES

Model Data with TypeScript Unions Instead of Optional Fields

An interface full of optional fields allows combinations that should never exist. Discriminated unions describe each valid state, and the compiler then checks that your code handles all of them.

On this page

The core idea

Optional fields are tempting: a request type might have data?, error? and loading?. But that type also allows a request that is loading and has an error and data at the same time. A discriminated union instead lists each valid state with a shared literal field, often called status or kind. TypeScript narrows the type when you check that field, so inside each branch only the properties that really exist are available. Adding a new state then produces compiler errors wherever the code has not handled it yet.

A practical approach

Step 1

List the real states before writing types. For a data request these might be idle, loading, success with data and failure with an error message.

Step 2

Give each state a literal discriminant: type RequestState = { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: Course[] } | { status: 'error'; message: string }.

Step 3

Handle the state with a switch on status. Inside each case, TypeScript knows exactly which fields exist.

Step 4

Add an exhaustiveness check in the default branch: const unreachable: never = state. If someone adds a new status, this line stops compiling until it is handled.

Step 5

Create values through small helper functions so invalid combinations are hard to construct in the first place.

Worked example

A webinar checkout component used one type with optional orderId, paymentError and isPaying fields. A bug allowed isPaying to stay true after a failed payment, so the Pay button stayed disabled forever. Rewriting the state as a union of 'ready', 'paying', 'paid' with an orderId and 'failed' with a message made that combination impossible to represent. The switch statement now forces the UI to decide what to show for every state, including failure.

A mistake to avoid

Do not use type assertions such as state as SuccessState to get past narrowing. An assertion tells the compiler to trust you and removes exactly the protection the union was meant to provide.

Try it yourself

Take a component or function in your project that uses three or more optional fields to represent progress or results. Rewrite its type as a discriminated union, add an exhaustiveness check and note how many impossible combinations the old type allowed.

Your implementation checklist

  • Each valid state is a separate member of a union
  • States share a literal discriminant field
  • Switch statements narrow on the discriminant
  • An exhaustiveness check catches unhandled new states
  • No type assertions are used to bypass narrowing

Share or cite this guide

Innovate with Ajay Editorial (2026-10-07). Model Data with TypeScript Unions Instead of Optional Fields. https://innovatewithajay.com/blogs/model-data-with-typescript-unions

Editorial approach, sources and corrections

YOUR NEXT CHAPTER

You have the curiosity.
Let’s turn it into capability.

Start with a practical workshop or bring a specific question to Ajay.

Chat with Ajay