Extending & Combining Types
Intermediate · 20 min read · ▶ live playground · ✦ checkpoint
Extending and combining types let you build one object shape from smaller shapes instead of copying the same fields everywhere. Use interface extends when you are already in interface-land; use intersections with & for the type-first app-code pattern this course now follows.
The idea is ordinary: a launch approval, a support ticket, and a billing receipt may all share an id, an owner, and a timestamp. You want one reusable base promise, then specific shapes that add their own details.
interface extends
Inheritance means one shape starts with another shape's properties and adds more. With interfaces, the syntax is extends, and it reads like a form that copies all fields from a base form before adding a new section.
LaunchApproval has four required properties. Two are written directly on it; two come from TrackedItem. Nothing here creates a class, constructor, or runtime parent object. This is still the type layer only.
Because 5.1 set the course rule, we won't switch all app data to interfaces just because extends exists. You still need to read this syntax in library and older code, and you should use it when an interface contract is already the right tool.
Intersections with &
An intersection type uses & to require a value to satisfy multiple types at once. Reuse the course's set model: A & B is the overlap of the allowed values in A and B.
For object shapes, that usually means "all properties from both":
This is the type-first version of the same composition. It also scales past object shapes, because type aliases can name unions, tuples, function types, and intersections under one habit.
Do not read & as "either." That is |, the union operator from Section 4. A & B means both promises at once. If a value is missing one side, it is outside the intersection.
The silent never trap
never is the empty set: no possible value can satisfy it. Intersections can create never when two promises fight each other.
This is clear with primitives: string & number is never, because no value is both a string and a number. The object-property version is sneakier. The alias line looks fine, but one property has become impossible:
type ApiLaunch = {
id: string;
status: "queued" | "live";
};
type DatabaseLaunch = {
id: number;
savedAt: string;
};
type BrokenLaunch = ApiLaunch & DatabaseLaunch;
const launch: BrokenLaunch = {
id: "L-42",
status: "queued",
savedAt: "09:00",
};→ playground.ts:14:3 - error TS2322: Type 'string' is not assignable to type 'never'.
When you see never in a property you did not write as never, look for an intersection conflict. Two base shapes probably used the same property name for different meanings. Rename one side, split the shape, or write one intentional property type instead of smashing incompatible promises together.
Base plus variant shapes
Composition means building a larger type from smaller pieces. A variant is one member shape in a union, like a bug ticket or launch ticket. The clean pattern is: write a base shape once, then intersect it with each variant's own fields.
This pattern keeps duplication low without hiding the important differences. TicketBase holds the fields every ticket has. Each variant adds a literal kind plus its own data, so narrowing from Section 4 still works cleanly.
The pressure to over-abstract starts early here. If two shapes share one property, do not create a base type just to feel tidy. Reach for base-plus-variant composition when there is a real shared concept readers would name in the product.
Next up, you meet the rule that makes all this feel different from many typed languages: TypeScript compares shapes, not names. That rule explains why two separate types can still accept the same object.
Checkpoint
Answer all three to mark this lesson complete