TypeScript Types vs Interfaces Explained
SkillVeris Team
Engineering Team

Type aliases and interfaces both describe the shape of data in TypeScript, and for plain objects they are largely interchangeable.
In this guide, you'll learn:
- Interfaces can be extended and reopened through declaration merging; type aliases cannot be reopened but can express unions, tuples, and mapped types.
- Use interface for object shapes and public APIs that may be extended; use type for unions, primitives, and complex compositions.
- Interfaces support declaration merging, which libraries use to let you augment their types.
- Type aliases can represent things interfaces cannot, like string | number unions or tuple types.
1Types vs Interfaces in TypeScript
In TypeScript, both a type alias and an interface let you describe the shape of an object, and for most everyday object definitions they are interchangeable. The differences appear at the edges: interfaces can be reopened and merged, while type aliases can express unions, tuples, and other constructs interfaces cannot.
A type alias uses the type keyword to name any type, including primitives and unions. An interface uses the interface keyword and is designed specifically for object shapes and class contracts. Knowing where they diverge helps you pick the right tool without overthinking it.
2The Basic Syntax
Both describe an object with named, typed properties. Side by side, the declarations look almost identical for a simple shape.
- interface User { id: number; name: string; } # interface form
- type User = { id: number; name: string; } # type alias form
- Both can be used to type variables, function params, and return values.
- type also aliases non-objects: type Id = string | number;
🔑Interchangeable for Objects
For a plain object shape, interface and type produce the same result. The choice only starts to matter when you need extension, merging, or unions.
3Extension and Declaration Merging
Interfaces have two abilities type aliases lack: they extend with the extends keyword, and they support declaration merging, where two interfaces of the same name combine automatically.
Extending
Interfaces extend cleanly, and type aliases compose the same idea with intersections. Both work, but the syntax differs.
interface Admin extends User { role: string; } # interface extension
type Admin = User & { role: string; } # type intersection
Both add role on top of the User shape.Declaration Merging
Two interfaces with the same name merge into one. Libraries rely on this so you can augment their built-in types, such as adding a property to an existing global interface. Type aliases cannot be reopened this way — a duplicate name is an error.
interface Window { myGlobal: string; } # merges into the existing Window type4What Only Type Aliases Can Do
Type aliases can name shapes that are not objects at all, which makes them essential for modeling many real-world values. This is the main reason you cannot use interfaces for everything.
- Union types: type Status = 'idle' | 'loading' | 'error'
- Tuple types: type Point = [number, number]
- Primitive aliases: type Id = string
- Mapped and conditional types for advanced transformations.
- Function types read naturally: type Handler = (e: Event) => void
💡Reach for Type for Unions
Whenever you need a union, tuple, or mapped type, a type alias is the only option. Interfaces are limited to object and class shapes.
5Common Mistakes to Avoid
The type-versus-interface debate causes more confusion than bugs, but a few missteps still trip people up.
- Debating the choice endlessly instead of picking one convention and moving on.
- Trying to create a union with an interface, which is not possible.
- Accidentally merging two interfaces with the same name and getting a combined shape you did not intend.
- Assuming they behave identically in error messages — interface errors are sometimes clearer.
- Mixing both styles inconsistently across a codebase without a rule.
6Which Should You Use?
A simple rule keeps most codebases consistent. Use interface for object shapes and public APIs, and type when you need capabilities interfaces lack.
- Use interface for object shapes, class implements contracts, and library types meant to be extended.
- Use type for unions, tuples, primitives, function types, and complex compositions.
- Pick one style as the default and only switch when the other is required.
- Follow the convention already in your codebase or linter config over personal preference.
7Extending Third-Party Types
One practical place the difference matters is augmenting types from libraries. Because interfaces support declaration merging, libraries expose interfaces you can reopen to add your own properties, such as extending a request object or a global namespace.
If a package defined its type as a type alias, you could not merge into it — you would have to wrap it in a new type with an intersection. This is why library authors often prefer interfaces for public, extensible types, while keeping type aliases for internal unions and helpers.
- declare module 'express' { interface Request { user?: User } } # merge into a library interface
- Merging requires an interface; a type alias cannot be reopened.
- For your own extension of a type alias, compose with an intersection instead.
- Prefer exposing interfaces from a library's public API for this reason.
8Key Takeaways
The differences are real but narrow. These points capture what actually matters day to day.
- For plain object shapes, interface and type are largely interchangeable.
- Interfaces support extension and declaration merging; type aliases do not reopen.
- Only type aliases can express unions, tuples, primitives, and mapped types.
- Prefer interface for extensible object and class contracts, type for everything else.
- Consistency across a codebase matters more than the theoretical distinctions.
9Frequently Asked Questions
Q: Are interfaces and types the same in TypeScript? A: For describing plain object shapes they are largely interchangeable and compile to the same checks. They differ in that interfaces support declaration merging and are conventionally used for extensible contracts, while type aliases can also express unions, tuples, and primitives.
Q: Can I create a union type with an interface? A: No. Union types like 'idle' | 'loading' can only be written with a type alias. Interfaces are limited to object and class shapes, so any union, tuple, or primitive alias requires the type keyword.
Q: What is declaration merging? A: Declaration merging is when two interfaces with the same name in scope automatically combine into a single interface. Libraries use it to let you augment their types, such as adding a property to a global interface. Type aliases cannot be merged this way.
Q: Which should a beginner use? A: Start with interface for object shapes because the syntax is clear and it extends naturally, and reach for type when you need unions, tuples, or other non-object types. Most importantly, follow whatever convention your project already uses.
Related Reading
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering writers turn abstract code concepts into hands-on, project-driven learning experiences.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.