Menu

TypeScript Interface vs Type: Differences and When to Use

interface and type can both describe object shapes, and most of the time either works. Learn the real differences: declaration merging, unions and mapped types, extends vs intersections, implicit index signatures, error reporting and compiler performance, plus a clear rule for choosing.

This page includes runnable editors - edit, run, and see output instantly.

For describing the shape of an object, interface and type do the same job, and values of one are assignable to the other when the shapes match. The differences are at the edges: type can name things an interface cannot (unions, tuples, computed types), and an interface can do a few things a type alias cannot (merging, checked extends).

TypeScript compares object types by structure, so the name of the declaration does not matter for assignability. What follows is the list of places where the choice does matter.

Comparison Table

Featureinterfacetype
Object shapesyesyes
Optional, readonly, methods, index signaturesyesyes
Genericsyesyes
Unions (A | B)noyes
Tuples, primitives, function types by themselvesno (function types only as call signatures)yes
Mapped and conditional typesnoyes
Extendingextends, conflicts are errors&, conflicts become never
Declaration mergingyesno (duplicate identifier)
Assignable to Record<string, T>noyes, when the properties fit
implements in a classyesyes, if it is an object type
Recursive definitionsyesyes

What Only type Can Do

Anything that is not a single object shape needs a type alias:

None of these can be written with interface (the last two are covered in mapped types and conditional types). This is the practical reason every codebase ends up using type somewhere, whatever it uses for object shapes.

What Only interface Can Do: Declaration Merging

Two interface declarations with the same name in the same scope combine into one. Two type declarations with the same name are error TS2300, Duplicate identifier.

interface Settings {
  theme: string;
}
interface Settings {
  fontSize: number;
}
const s: Settings = { theme: "dark", fontSize: 14 }; // needs both

type Options = { a: number };
type Options = { b: number }; // error TS2300: Duplicate identifier 'Options'.

Merging is how library types are extended from outside: adding a property to the global Window, to Express's Request, or to a library's theme type. If you publish types that users may need to augment, use interfaces. In your own application code, an accidental merge (two script files declaring the same global interface name) happens silently unless the two declare the same property with different types, which is one argument some teams give for preferring type.

extends vs Intersection

An interface extends with extends, a type alias with &. They mostly produce the same result, but they handle a conflicting property differently. extends reports the conflict at the declaration:

index.ts(7,11): error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
  Types of property 'id' are incompatible.
    Type 'number' is not assignable to type 'string'.

An intersection accepts the same conflict silently and turns the property into never (a string that is also a number). The error only appears later, when you try to create a value:

The error points at the object, far from the declaration that caused it. For building object types from other object types, extends gives the better message.

Index Signatures: an Easy-to-Miss Difference

A type alias for an object type gets an implicit index signature, so it can be passed where a Record<string, unknown> is expected. An interface does not. This is by design: the TypeScript team's explanation (issue #15300 on the TypeScript repository) is that an interface can be augmented by later declarations, so inferring an index signature for it is less safe, and changing the rule now would break too much code.

The call marked @ts-expect-error still runs and prints Alan's fields: the rule is a compile-time one. This is the usual reason for a confusing error when an interface value is passed to a logging, serialization or query helper typed with Record<string, ...>. Switch that one declaration to type, spread the value, or type the helper's parameter with an interface or a generic instead.

Performance and Error Messages

The Performance page of the TypeScript wiki (section "Preferring Interfaces Over Intersections") suggests interface Foo extends Bar, Baz { ... } over type Foo = Bar & Baz & { ... } when composing object types. Its reasons: an interface is a single flat object type that detects property conflicts, type relationships between interfaces are cached (intersections as a whole are not), and checking a value against an intersection checks every constituent before the flattened type. The difference matters in large codebases with many composed types; for a few ordinary object shapes you will not measure it.

The same page notes that interfaces display better. An interface is shown by its name in hovers and error messages, while an intersection alias is often printed expanded into its parts, which makes long messages harder to read.

Which One to Use

A rule that works:

  1. Object shapes: interface. It gives checked extends, clearer errors on large compositions, and lets library users augment it. This matches the TypeScript handbook's heuristic: "use interface until you need to use features from type".
  2. Everything else: type. Unions, tuples, function types, literal types, and anything built with mapped, conditional or template literal types.
  3. Exception: use type for an object shape that has to fit Record<string, ...> parameters, or when you deliberately want to prevent merging.

Using type for everything is also a coherent choice, and many codebases do. Mixing both at random is the one option to avoid: it makes readers wonder whether a difference was intended.

Frequently Asked Questions

What is the difference between type and interface in TypeScript?

Both describe object shapes and are interchangeable for that. type can also name unions, tuples, primitives, and mapped or conditional types, which interface cannot. interface supports declaration merging and extends, which checks for conflicting properties. Object types written with type are also assignable to index-signature types like Record<string, unknown>, while interfaces are not.

Should I use type or interface?

The TypeScript handbook's heuristic is: use interface until you need a feature only type has. In practice that means interfaces for object shapes and type for unions, tuples, function types and computed types. Teams that use type for everything also do fine; the important part is one consistent rule.

Is interface faster than type in TypeScript?

For composing object types, yes in some cases. The TypeScript team's performance guidance recommends interface ... extends over large intersections (A & B & { ... }), because relationships between interfaces are cached and an interface is one flat type. For a simple object shape there is no meaningful difference.

Can a class implement a type alias?

Yes, if the alias is an object type (or an intersection of object types): class Point implements PointType { ... } works. A class cannot implement a union type; that is error TS2422.

Can an interface extend a type alias?

Yes, as long as the alias is an object type: type Base = { id: string }; interface User extends Base { name: string } is valid. It cannot extend a union alias. In the other direction, a type alias can build on an interface with &.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED