03open 25 min

Data and state modeling: object types, interfaces, unions, and safer API shapes

Move from loose JavaScript objects toward explicit TypeScript models for handlers, payloads, and API results.

by the end of this lesson you can

  • Models result states deliberately instead of as one loose object
  • Uses object types or unions to improve the caller contract
  • Avoids hiding invalid state combinations behind optional fields

Overview

JavaScript and Node code often evolves around flexible object literals and conventions that teams learn over time. TypeScript is most valuable when it turns those conventions into explicit shapes, especially at API boundaries and in stateful application flows.

In JavaScript/Node, you often

pass around objects whose expected fields are known socially or by tests more than by the function signature itself.

In TypeScript, the common pattern is

to define object types, interfaces, and unions that make legal payloads and states obvious to both the compiler and the next maintainer.

why this difference matters

This lesson is the heart of the JS/Node-to-TypeScript transition. Better object modeling is where teams feel safer refactors and fewer accidental interface breaks.

JavaScript/Node

function handleRequest(req) {
  return { id: req.params.id, ok: true };
}

TypeScript

type RequestParams = { id: string };

type HandlerResult = {
  id: string;
  ok: true;
};

function handleRequest(req: { params: RequestParams }): HandlerResult {
  return { id: req.params.id, ok: true };
}

Deeper comparison

JavaScript/Node version

const event = { type: "user.created", id: "1" };

TypeScript version

type Event =
  | { type: "user.created"; id: string }
  | { type: "user.deleted"; id: string };

Reflect

Why is an explicit union or object type often a better fit than a broad shape-ish object in a growing Node codebase?

what a strong answer notices

A strong answer mentions safer public contracts, fewer accidental optional-field assumptions, and clearer signaling about which object variants are actually legal.

Rewrite

Rewrite this JavaScript API result shape into TypeScript so callers can tell what is guaranteed versus optional.

Rewrite this JavaScript/Node

return { ok: true, user, error: null };

what good looks like

  • Models result states deliberately instead of as one loose object
  • Uses object types or unions to improve the caller contract
  • Avoids hiding invalid state combinations behind optional fields

Practice

Design TypeScript types for an event emitter payload contract with three event names and distinct payload shapes.

success criteria

  • Defines payload shapes precisely
  • Shows where unions or mapped object types improve safety
  • Keeps the resulting API recognizable to a JavaScript/Node team

Common mistakes

  • Keeping one giant object type with too many optional fields because it feels close to the original JavaScript.
  • Using Record<string, unknown> where the code really knows much more.
  • Treating interfaces as documentation only instead of as public contract definitions.

takeaways

  • This lesson is the heart of the JS/Node-to-TypeScript transition. Better object modeling is where teams feel safer refactors and fewer accidental interface breaks.
  • A strong answer mentions safer public contracts, fewer accidental optional-field assumptions, and clearer signaling about which object variants are actually legal.
  • Defines payload shapes precisely