跪拜 Guibai
← All articles
TypeScript

TypeScript's type vs. interface: The Rules That Actually Matter

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

Picking `type` over `interface` — or vice versa — is not just stylistic. Using `type` for a global augmentation silently fails; using `type` for conflicting property extensions silently produces `never` instead of a compile error, hiding bugs that `interface` would catch immediately.

Summary

Both `type` and `interface` describe object structures and function signatures, and both support extension — `interface` via `extends`, `type` via intersection `&`. The overlap is broad enough that many codebases mix them without a clear rule. But the differences are concrete and consequential. `interface` supports declaration merging: multiple same-name declarations combine into one type, which is essential for patching global types like `Window`. `type` forbids duplicate names entirely. `type` can alias primitives, unions, and tuples; `interface` is restricted to object-like structures. For function types, `interface` uses a call-signature syntax that makes attaching extra properties natural, while `type` uses a cleaner arrow syntax but requires intersection for the same effect. The most dangerous difference is conflict behavior: `interface` throws a compile error on incompatible property extensions, but `type` silently resolves the conflict to `never` — a type that accepts no value. The practical rule of thumb that emerges is to use `interface` for public library APIs and global augmentations, and `type` for internal aliases, unions, and simple function signatures.

Takeaways
`interface` supports declaration merging — multiple same-name declarations combine into one type — while `type` throws a duplicate-identifier error.
`type` can alias primitives, unions, and tuples; `interface` is limited to object-like structures.
`interface` uses call-signature syntax for functions, making it natural to attach extra properties; `type` uses arrow syntax, which is cleaner for simple signatures but requires intersection for extra properties.
Property conflicts in `interface` extensions produce a compile error; in `type` intersections, the conflicting field silently becomes `never`.
Use `interface` for public library APIs and global type patches; use `type` for internal aliases, unions, tuples, and simple function signatures.
Conclusions

The `never` resolution in `type` intersections is a footgun: it compiles without complaint but makes the field unusable, so a team that defaults to `type` for everything risks shipping types that look correct but reject every value.

Declaration merging is the single capability that makes `interface` non-negotiable for library authors and global augmentations — there is no `type` workaround, so the choice is forced, not preferential.

The community convention of `interface` for public APIs and `type` for internals is less about dogma and more about surfacing errors early: `interface` catches extension conflicts at compile time, which matters most at API boundaries.

Concepts & terms
Declaration Merging
A TypeScript feature where multiple `interface` declarations with the same name are automatically combined into a single type. Used for augmenting global types or patching third-party type definitions.
Intersection Type (`&`)
A TypeScript operator that combines multiple types into one. When used with `type`, conflicting properties resolve to `never` rather than producing a compile error.
Call Signature
The `interface` syntax for describing a function type — written as `(params): ReturnType` inside curly braces. It allows attaching extra properties directly to the function type definition.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗