Data and state modeling: interfaces, type aliases, unions, and discriminated unions
Move from Python's dynamic data shaping into explicit TypeScript models for objects, variants, and state transitions.
by the end of this lesson you can
- →Separates shape definition from runtime value clearly
- →Uses interface or type alias intentionally
- →Makes optional versus required fields explicit instead of implied
Overview
Python developers are used to dicts, classes, dataclasses, and conventions that stay flexible until runtime. TypeScript rewards you for deciding earlier what shapes exist, which variants are legal, and how state changes should be represented in the type system.
In Python, you often
model evolving data with dicts, dataclasses, or informal conventions that callers are expected to respect.
In TypeScript, the common pattern is
to make object shapes and variant states explicit using interfaces, type aliases, unions, and discriminated unions.
why this difference matters
This is where TypeScript becomes more than annotated JavaScript. Good data modeling catches ambiguity early and replaces many I hope this dict has the right keys assumptions with readable contracts.
Python
response = {"status": "ok", "data": user}TypeScript
type ApiResponse =
| { status: "ok"; data: User }
| { status: "error"; message: string };Deeper comparison
Python version
event = {"kind": "signup", "email": "ana@example.com"}TypeScript version
type Event =
| { kind: "signup"; email: string }
| { kind: "purchase"; orderId: string };Reflect
Why can a discriminated union be a better model for dynamic application data than a Python-style flexible dictionary?
what a strong answer notices
A strong answer mentions explicit legal variants, better narrowing in callers, and fewer hidden assumptions about which keys exist together.
Rewrite
Rewrite this Python-style config shape into TypeScript so optional and required fields are modeled explicitly.
Rewrite this Python
config = {"region": "us", "timeout": 30, "debug": False}what good looks like
- Separates shape definition from runtime value clearly
- Uses interface or type alias intentionally
- Makes optional versus required fields explicit instead of implied
Practice
Design TypeScript types for an API client that can return loading, success, or error states without leaving the caller to guess which fields exist.
success criteria
- Uses a discriminated union or equivalent explicit variant model
- Keeps each state shape easy to understand
- Shows how the caller would branch safely on the result
Common mistakes
- Treating interfaces as mere replacements for Python dict annotations instead of as contract tools.
- Collapsing multiple states into one broad object type full of optional fields.
- Reaching for classes when a union of object shapes would communicate the state model more clearly.
takeaways
- ●This is where TypeScript becomes more than annotated JavaScript. Good data modeling catches ambiguity early and replaces many I hope this dict has the right keys assumptions with readable contracts.
- ●A strong answer mentions explicit legal variants, better narrowing in callers, and fewer hidden assumptions about which keys exist together.
- ●Uses a discriminated union or equivalent explicit variant model