07 — Interfaces: Naming the Shape of Objects
"Just another spelling for a type" was my interface read, which hid what makes it special. Writing it down separated the concerns: an interface is a named contract describing the shape an object must have, it produces no JavaScript, and TypeScript's type system is bounded at the bottom by never and at the top by unknown. [1] Each of those ideas earns its own attention.
The starting claim is mechanical. An interface declares a set of property names, their types, and whether each is optional. Any object that matches the shape satisfies the interface, no implements keyword required (that's structural typing again). And the interface itself vanishes at compile time — it exists only for the type checker [1].
interface User {
id: number;
name: string;
email?: string; // optional
}The ? marks email as optional; everything else is required. Any object with at least id and name of the right types satisfies User, regardless of what else it carries.
never and unknown: the two ends
Two types bookend the whole system, and understanding them clarified a lot.
never is the bottom type — the type of values that never occur [2]. A function that always throws or runs forever implicitly returns never. You can't assign anything to never (except never itself). Its practical use is exhaustive checking: in a discriminated union with a finite set of cases, assigning the "default" branch to never makes the compiler error if a new case is added but not handled. never is how I make the compiler enforce that every possibility is covered.
unknown is the top type — the type of values that could be anything [3]. Unlike any, which disables checking, unknown forces me to narrow before use. It's the type-safe way to hold data I don't yet understand: parsed JSON, untyped API responses, values crossing trust boundaries. The hierarchy is never (nothing) at the bottom, concrete types in the middle, unknown (everything) at the top — and any is the unsafe escape hatch that sits beside unknown but skips the narrowing requirement.
any: the escape hatch
any opts a value out of the type system entirely [4]. I can do anything to it, assign it anywhere, and the compiler stays silent. It's occasionally necessary — untyped third-party libraries, rapid prototyping — but every any is a hole where errors hide. The relationship worth remembering: unknown is the safe version of "I don't know the type" (it demands narrowing), while any is the lazy version (it demands nothing). I reach for unknown and treat any as a deliberate, commented choice.
Types vs. interfaces
Both declare object shapes, and for most uses they're interchangeable — but the differences are real [5]:
- Interfaces describe object and class shapes; they support declaration merging (two declarations of the same interface name combine), which makes them the standard for library type definitions and things that need to be extended by consumers.
- Type aliases can describe unions, intersections, primitives, and conditional types — shapes interfaces can't express.
The rule I use: reach for an interface for plain object and class shapes, especially anything others might need to augment; reach for a type alias for unions, intersections, or anything involving computed types. When a shape is purely an object and I'm unsure, the interface is the conservative default because of its extensibility story.
Extending interfaces
Interfaces can inherit from one another, building a new interface on top of an existing one [6]:
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }A Dog has both name (from Animal) and breed. This is how I build hierarchies of shapes without repeating fields, and it's the interface analogue of class inheritance. Multiple inheritance is allowed — interface X extends A, B — combining several contracts.
Declaration and hybrid types
An interface declaration is simply the act of introducing one of these named contracts into the type system [6]. Because interfaces can merge, two declarations with the same name in different files combine their members — this is how libraries let consumers augment their types (more on that in the modules notes).
Hybrid types describe objects that act as both an object (with properties) and a function (callable). They arise when a function needs to carry associated state — a function with attached properties. They're uncommon but the right tool when a callable thing also needs metadata hanging off it.
How I use this
My defaults: reach for an interface for object/class shapes I expect to extend or that others might augment; reach for a type alias for unions, intersections, and computed types; treat never as my exhaustive-check enforcer in discriminated unions; treat unknown as the safe holder for untrusted data; and treat any as a last resort I'd rather justify than default to. The mental shift that stuck: interfaces and aliases aren't competing spellings — they're tools with different strengths, and never/unknown aren't exotic edge cases but the load-bearing ends of the whole type system.
References
[1] Microsoft, "Object Types," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/2/objects.html
[2] Microsoft, "The never type," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-never-type
[3] Microsoft, "New unknown top type," TypeScript 3.0 Release Notes, 2018. [Online]. Available: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type
[4] Microsoft, "any," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any
[5] Microsoft, "Differences Between Type Aliases and Interfaces," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces
[6] Microsoft, "Extending Interfaces," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/2/objects.html
Knowledge check · Question 1 of 5
An interface declared in a TypeScript file produces what JavaScript at runtime?
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!