跪拜 Guibai
← All articles
TypeScript · Frontend · Interview

TypeScript's type vs. interface: The Real Difference Is Inheritance vs. Type Algebra

By dzhd ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Choosing between `type` and `interface` shapes how a codebase handles extension, augmentation of third-party types, and non-object type expressions. Picking the wrong one locks you out of declaration merging or forces awkward workarounds for union and tuple types.

Summary

Both `type` and `interface` describe object shapes, but `interface` uses `extends` for nominal-style inheritance while `type` uses `&` for structural intersection—a type-algebra operation that merges property sets without declaring subtype relationships. `interface` alone supports declaration merging, letting you augment third-party types without modifying source code. `type` alone can express union types, tuples, and literal types that `interface` cannot. For function types, `type`'s arrow-style syntax is more concise than `interface`'s call-signature approach. In React codebases, `interface` dominates component Props because its `extends` semantics align with the contract model between parent and child components, and its declaration merging allows seamless augmentation of library types. The practical rule: reach for `interface` when defining object contracts like component Props, and `type` for utility types, unions, and type-level transformations.

Takeaways
`interface` and `type` both describe object shapes with equivalent capability at the syntax level.
`interface` uses `extends` for inheritance, modeling a nominal subtype relationship; `type` uses `&` for intersection, which is a structural merge of property sets.
Only `interface` supports declaration merging—reopening the same name adds members, which is critical for augmenting third-party type definitions without forking.
Only `type` can express union types, tuples, literal types, and mapped types; `interface` is limited to object shapes.
Function types are possible with both, but `type`'s arrow-style syntax reads more naturally than `interface`'s call-signature object form.
React component Props are conventionally defined with `interface` because `extends` models the parent-child data contract and declaration merging allows extending library Props.
A practical heuristic: use `interface` for object contracts (component Props, public APIs), and `type` for utility types, unions, and type-level operations.
Conclusions

The `extends` vs. `&` distinction is not just syntactic sugar—it encodes two different type-system philosophies: nominal typing inherited from Java-style OOP versus structural type algebra. This choice surfaces in code-review discussions about whether a type should express an is-a relationship or a has-these-properties merge.

Declaration merging is the feature that makes `interface` strategically important in library and platform code, but it is also a footgun: accidental merges can silently widen types in ways that are hard to debug.

The React community's near-universal preference for `interface` Props is partly convention and partly a real constraint—declaration merging lets teams extend library component types without waiting on upstream PRs, which matters at scale.

Concepts & terms
Declaration Merging
A TypeScript compiler feature where multiple `interface` declarations with the same name are automatically combined into a single interface with the union of all members. This allows augmenting types defined in third-party libraries or across files without modifying the original source.
Intersection Type (`&`)
A type operator that combines multiple types into one by merging their property sets. Unlike `extends`, it does not establish a nominal subtype relationship; it is a structural operation that produces a type requiring all properties from each constituent type.
Nominal vs. Structural Typing
Nominal typing checks type compatibility based on explicit declarations (e.g., `extends`), while structural typing checks compatibility based on the shape of the type. TypeScript is structurally typed overall, but `interface extends` introduces a nominal-style relationship that the compiler can use for richer error messages and refactoring.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗