跪拜 Guibai
← All articles
Frontend · TypeScript

TypeScript's type vs. interface: The Six Differences That Decide Your Code

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

Choosing the wrong construct locks a codebase out of declaration merging when it's needed or forces awkward workarounds for union and tuple types. Knowing the six differences — especially the `never` trap in type intersections and the error behavior in interface extension — prevents bugs that compile silently and surface only at runtime.

Summary

The core split is philosophical: `interface` defines an open, extensible contract that supports declaration merging, while `type` is a closed alias built for composition and type-level computation. That distinction plays out across six concrete dimensions — from union and tuple support to how each handles conflicting property types during extension.

When `interface` extends a parent with an incompatible same-name property, TypeScript throws an error immediately. A `type` intersection with the same conflict silently resolves to `never`. Declaration merging is exclusive to `interface` and is the only way to safely augment third-party or global types like `Window`.

In practice, `type` dominates everyday business logic because it handles unions, mapped types, and conditional types that `interface` cannot express. `interface` remains essential for public API contracts, library type declarations, and any surface that needs to stay open for downstream augmentation.

Takeaways
`interface` can only describe object shapes; `type` handles primitives, unions, tuples, and the full range of TypeScript's type system.
During extension, `interface` throws an error on incompatible same-name properties, while `type` intersections silently collapse the property to `never`.
Declaration merging is exclusive to `interface` — duplicate `type` identifiers are a hard error — making `interface` the only safe way to augment global or third-party types.
All advanced type programming — mapped types, conditional types, `infer`, template literal types — requires `type`; `interface` supports none of it.
`interface` call signatures can describe a callable function that also carries properties and methods, a pattern common in library and utility types.
A class can `implements` an object-type `type` but not a union `type`; `interface` avoids this restriction entirely.
Community practice has shifted toward `type` for everyday flexibility, reserving `interface` for public contracts and library declarations that benefit from extensibility.
Conclusions

The `never` resolution in type intersections is a silent footgun: code compiles but the property becomes unusable, with no warning that a logic error occurred.

Declaration merging is simultaneously `interface`'s superpower and its design constraint — it makes global augmentation possible but also means an interface is never truly closed, which can complicate reasoning in large codebases.

The shift toward `type` in daily use reflects a broader TypeScript trend: developers are treating the type system as a programming layer, not just a contract-definition tool, and `type` is the native language of that layer.

Concepts & terms
Declaration Merging
A TypeScript behavior where multiple `interface` declarations with the same name are automatically combined into a single interface. This is the mechanism used to extend global types like `Window` or augment third-party library types without modifying their source.
Call Signature
An `interface` syntax that describes a function's parameter and return types while also allowing the same interface to declare properties and methods on that function. It defines a callable object, not just a bare function signature.
Intersection Type (`&`)
A `type` operator that combines multiple types into one. When two intersected object types define the same property with incompatible types, the result is `never` rather than a compile error — a behavior that differs critically from `interface extends`.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗