3 TypeScript Union Types Mistakes Parsing Dynamic JSON

Stop Hardcoding Union Types from JSON You Already Have

Most developers do this: they receive a JSON response from an API, glance at the possible values for a field like status, then manually type out type Status = 'pending' | 'active' | 'completed' | 'canceled' directly in their TypeScript file. It feels productive. It feels precise. And six weeks later, when the backend team adds a fifth status called 'suspended', the TypeScript compiler has no idea. Your union type silently lies. A runtime value slips through. Production breaks.

This is the single most common mistake I see when helping teams set up TypeScript converter workflows. You already have the JSON data sitting right there — in a fixture file, an API response log, or a configuration payload — yet you're transcribing it by hand into a union type as if the data doesn't exist. Every manual transcription is a future bug waiting to happen.

Let's fix this properly, step by step.

Why Manual Union Types Always Drift

Here's the core problem. When you hand-write a union type from JSON you've inspected once, you're creating a static snapshot of dynamic data. The JSON is the source of truth. Your type is a copy. And copies drift.

I worked with a team that had 47 enum-like string fields across their API models. They maintained all 47 union types by hand. When we audited their codebase against the actual API responses, 12 of those types were missing values that had been added to the API months earlier. That's a 25% drift rate. One in four union types was wrong, and nobody knew until a user-reported bug forced the investigation.

The fix is straightforward: derive your union types from the JSON data itself, and make that derivation part of your build process.

Step 1: Stop Inspecting JSON by Hand — Capture It as a Typed Fixture

The first correction is to stop treating JSON as something you look at and then type from memory. Instead, capture it as a fixture file that TypeScript can actually read and infer from.

Save your real API response or configuration JSON to a file:

// fixtures/api-response.json
{
  "users": [
    { "role": "admin", "status": "active", "plan": "enterprise" },
    { "role": "member", "status": "pending", "plan": "free" },
    { "role": "guest", "status": "canceled", "plan": "free" }
  ]
}

Now import it with resolveJsonModule enabled in your tsconfig.json. TypeScript will infer the literal types automatically. This is the foundation — the JSON is now a first-class participant in your type system, not just a reference document you eyeballed once.

Step 2: Extract Union Types Using keyof and typeof

Here's where most developers get stuck. They've imported the JSON, but they don't know how to extract a union of specific field values from it. Let me walk you through the exact technique.

Start by importing the JSON and capturing its type:

import apiResponse from './fixtures/api-response.json';

const users = apiResponse.users as const;

The as const assertion is critical here. Without it, TypeScript widens every string to string. With it, TypeScript preserves the literal values — 'admin', 'member', 'guest' — as distinct types.

Now extract the union type for a specific field:

type User = typeof users[number];

type Role = User['role'];
// Result: 'admin' | 'member' | 'guest'

type Status = User['status'];
// Result: 'active' | 'pending' | 'canceled'

type Plan = User['plan'];
// Result: 'enterprise' | 'free'

Let's break down what just happened. typeof users[number] gives you the union of all element types in the array — effectively, the shape of a single user object. Then User['role'] indexes into that shape and extracts the union of all literal values found in the role field across every object in the array.

No manual typing. No guessing. The union type is derived directly from the data.

Step 3: Handle the Real-World Mess — Missing Fields and Mixed Types

Here's where the patient instructor in me needs to pause and address reality. The technique above works beautifully when your JSON is clean and every object has the same shape. But real JSON data is messy. Some objects might be missing fields entirely. Some fields might contain null in one record and a string in another.

Let's say your fixture looks like this:

[
  { "role": "admin", "status": "active" },
  { "role": "member", "status": null },
  { "role": "guest" }
]

If you run the extraction from Step 2 on this data, Status will include null in the union, and the third object (missing status entirely) will cause TypeScript to infer status as an optional property. That's actually correct behavior — but you need to be deliberate about how you handle it.

The common mistake here is to either ignore the null values (creating a type that doesn't match reality) or to strip them out of the fixture (creating a type that's too narrow). Both approaches reintroduce the drift problem.

Instead, embrace the full union and then refine it intentionally:

type RawStatus = User['status'];
// 'active' | null | undefined

type StrictStatus = NonNullable<RawStatus>;
// 'active'

type OptionalStatus = RawStatus | undefined;
// 'active' | null | undefined (explicit)

This way, you're making a conscious decision about how to handle missing or null values, rather than letting an accident of your fixture data determine your type semantics.

Step 4: Scale This into a Repeatable Converter Pipeline

If you're working with more than a handful of JSON sources — and in any real project, you are — you need to turn this manual extraction process into something repeatable. This is where the TypeScript converter workflow comes in.

Create a single file that imports all your JSON fixtures and exports all derived union types:

// types/derived-from-json.ts
import usersFixture from '../fixtures/users.json';
import productsFixture from '../fixtures/products.json';
import ordersFixture from '../fixtures/orders.json';

const users = usersFixture as const;
const products = productsFixture as const;
const orders = ordersFixture as const;

export type UserRole = (typeof users)[number]['role'];
export type ProductCategory = (typeof products)[number]['category'];
export type OrderStatus = (typeof orders)[number]['status'];
export type PaymentMethod = (typeof orders)[number]['paymentMethod'];

Now every consumer across your codebase imports from this single source file. When the backend adds a new order status, you update one fixture file, and every dependent type updates automatically. No manual transcription. No drift.

For larger teams, you can go one step further and write a script that fetches the latest API responses on a schedule, saves them as fixtures, and runs tsc --noEmit to surface any type errors caused by schema changes. If the compiler passes, your types are still in sync. If it fails, you've caught the drift before it reaches production.

The Mindset Shift That Makes This Stick

The real correction here isn't just a syntax trick. It's a mindset shift. Stop thinking of TypeScript types as something you author independently of your data. Start thinking of JSON as the source and types as the derived output.

When you adopt this approach consistently — capturing real JSON, extracting unions with typeof and as const, handling nullability deliberately, and centralizing the derivation in one converter file — you eliminate an entire category of bugs. The kind where the compiler says everything is fine but reality disagrees.

Your JSON already knows what values are possible. Let it tell TypeScript.

Frequently Asked Questions

How do I convert JSON to a TypeScript union type?

You can convert JSON to a TypeScript union type by using an online TypeScript converter tool or by parsing the JSON keys and values programmatically. Simply paste your JSON data into the converter, and it will automatically generate the appropriate union types for your project.

Can I generate TypeScript types dynamically from JSON?

While TypeScript types are erased at runtime, you can generate them dynamically during development using schemas like JSON Schema or tools like Quicktype. These tools read your dynamic JSON data and output static TypeScript definitions, including union types, that you can import into your codebase.

How to create a union type from an array of JSON objects?

To create a union type from an array of JSON objects, you can use the `as const` assertion on your JSON array and then map over it to extract the desired keys. Alternatively, an online JSON-to-TypeScript converter can instantly analyze the array and generate a union type representing all possible object shapes.

Is it possible to extract union types from JSON keys in TypeScript?

Yes, you can extract union types from JSON keys by using the `keyof` operator combined with the `typeof` keyword on a constant JSON object. If you define your JSON data with `as const`, TypeScript will infer the literal types of the keys, allowing you to easily create a strict union type.

What tools convert JSON values to TypeScript union types?

There are several popular tools and online converters, such as Quicktype, JSON to TS, and Transform, that specialize in converting JSON values to TypeScript union types. These tools inspect your JSON payloads and automatically generate precise type definitions, saving you from manually typing out complex unions.

How to infer TypeScript types from a dynamic JSON response?

Inferring types directly from a dynamic runtime JSON response isn't natively possible in TypeScript because types are strictly compile-time. However, you can sample the dynamic JSON response, feed it into a TypeScript converter tool, and use the generated static types to safely handle the data in your application.

Can I make a union type from a JSON array of strings?

Yes, if you have a JSON array of strings, you can declare it in TypeScript using the `as const` assertion to lock the strings as literal types. Then, you can use a utility type like `(typeof myArray)[number]` to automatically generate a string union type from the array's values.

How to use `as const` to create union types from JSON?

The `as const` keyword tells TypeScript to treat your JSON object or array as completely immutable, inferring the most specific types possible for its values. By applying `as const` to your JSON data, you can easily extract literal values and form strict union types using the `typeof` and `keyof` operators.

How to convert a JSON object's values into a string union type?

You can convert a JSON object's values into a string union type by combining `Object.values` with the `as const` assertion and mapping the types. For a quicker solution, many online TypeScript converters allow you to paste the JSON object and select an option to extract value union types automatically.