Javascript is required
PitfallsPublished 2026-07-284 min read

JSON to TypeScript: Null, Optional and Union Types

JSON has no undefined value: a property can be absent or present with null. TypeScript ? means a property may be absent, while string | null means a present property may be null. Combining them is still different from undefined. This article covers those states, not OpenAPI code generation.

By: Tiny
JSON to TypeScriptNullOptionalUnion Types

1. Problem: missing and null are different inputs

{} and {"nickname":null} are different JSON documents. The first has no key; the second explicitly sends an empty value. A nickname: string | null type rejects absence, while nickname?: string cannot express an explicit null response.

2. Minimal reproduction: optional and nullable together

This type separates a required number, a required nullable nickname, and a note that may be absent or null. With strictNullChecks enabled, callers must handle the branches.

type Profile = {
  id: number;
  nickname: string | null;
  note?: string | null;
};
const explicitNull: Profile = { id: 1, nickname: null };
const missingOptional: Profile = { id: 2, nickname: "Ada" };
console.log(Object.hasOwn(explicitNull, "note")); // false
console.log(Object.hasOwn(missingOptional, "nickname")); // true

3. Root cause: optional access can produce undefined

Reading an optional property can produce undefined at runtime. Reading a required string | null property produces string or null. JSON.stringify also omits object properties whose value is undefined, so treating undefined as JSON null changes the wire contract.

4. Recommendation: model transport states explicitly

Decide whether each field is required, nullable, or allowed to have other types. Use field?: T | null when both absence and null are meaningful, field?: T for absence only, and field: T | null for required-but-nullable data. Do not hide the distinction with any.

5. Complete code: distinguish missing, null, and value

This function uses Object.hasOwn to distinguish an absent key from explicit null. Empty strings, 0, and false are not treated as missing.

type Input = { nickname?: string | null; count: number | null };
type FieldState = "missing" | "null" | "value";
function nicknameState(input: Input): FieldState {
  if (!Object.hasOwn(input, "nickname")) return "missing";
  return input.nickname === null ? "null" : "value";
}
console.log(nicknameState({ count: 0 })); // missing
console.log(nicknameState({ nickname: null, count: 0 })); // null
console.log(nicknameState({ nickname: "", count: 0 })); // value

6. Incorrect approaches

value || defaultValue treats empty strings, 0, and false as absent. value ?? defaultValue preserves those falsy values but still merges explicit null with absence. Making every optional field any removes compile-time guidance.

7. Boundaries: nested fields, arrays, and narrowing

If a parent object is absent, do not read a child directly. Array elements can independently be missing or null. Narrow unions with typeof, literal checks, or a type guard rather than bypassing the check with an assertion.

8. Verification

Prepare missing, explicit null, empty string, 0, false, and normal JSON values. Check Object.hasOwn, runtime values, JSON.stringify output, and TypeScript diagnostics. If an API omits null fields, implement that policy explicitly in serialization.

9. FAQ

Is field?: string the same as field: string | undefined? Access can look similar, but optional also describes key presence and compiler options such as exactOptionalPropertyTypes affect assignments.

Can null and undefined be exchanged? JSON does not carry undefined as a value; conversion needs a contract.

Does a union mean every type must be accepted? Yes; callers must narrow it before use.

10. Summary

Missing fields, null, optional properties, and unions describe presence, emptiness, and allowed type sets. Use ?, | null, and explicit guards, preserve 0/false/empty strings, and verify the boundary samples with strictNullChecks.

Sources and further reading

Specifications and references for checking the article’s technical details.

Related articles

Keep exploring

Use the related browser tool to test the workflow with your own sample data.

Open related tool