TypeScript's type vs. interface: The Six Differences That Decide Your Code
In frontend interviews, the proportion of TypeScript-related questions has been increasing year by year, and "what is the difference between type and interface" is almost the first mandatory question in TS interviews.
Many candidates can only give superficial answers like "one uses extends for inheritance, the other uses & for merging," which makes it easy for interviewers to press them into silence. In fact, the difference between the two is essentially a difference in design philosophy: interface is a "contract-style interface definition," inherently open and extensible; type is a "flexible type alias," focused on versatility and composition.
This article thoroughly explains the commonalities and core differences between type and interface, from syntax features and design logic to practical selection, so that after reading, you can not only handle interviews but also use them more reasonably in projects.
1. First, Understand: What Do They Have in Common?
Many people jump straight to the differences. In fact, clarifying the commonalities first helps you better understand the positioning of both.
interface and type are both core syntaxes in TypeScript used to define type structures. Their core function is to enforce type constraints, and they are almost interchangeable in basic scenarios:
1. Both Can Describe Object Structures
Whether it's a plain object or an object with methods, both can achieve completely equivalent constraint effects:
// interface syntax
interface User {
id: number;
name: string;
sayHello(): void;
}
// type syntax
type User = {
id: number;
name: string;
sayHello(): void;
};
2. Both Can Define Function Types
Both can describe the parameter and return value types of a function, and can be used for type annotations of function parameters and return values:
// interface call signature syntax
interface AddFunc {
(a: number, b: number): number;
}
// type function type expression syntax
type AddFunc = (a: number, b: number) => number;
3. Both Support Generics and Index Signatures
Both can be used with generics and support index signatures to describe objects with dynamic keys:
// Generic support
interface Box<T> { value: T }
type Box<T> = { value: T };
// Index signatures
interface Dictionary { [key: string]: number }
type Dictionary = { [key: string]: number };
2. Core Differences: 6 Dimensions to Thoroughly Distinguish Them
After covering the commonalities, let's look at the core differences, which are also the key scoring points in interviews.
1. Type Coverage Scope: type is Versatile, interface Only Supports Objects
This is the most fundamental difference: interface can only be used to define object types, while type can define any type.
Scenarios that type supports but interface cannot do:
Primitive type aliases
// ✅ type can do this type UserId = string; type StatusCode = number; // ❌ interface does not support this, syntax is completely wrong interface UserId = string;Union types
// ✅ type can do this type Status = 'success' | 'error' | 'loading'; type Result = User | null; // ❌ interface cannot natively express union typesTuple types
// ✅ type can do this type DataTuple = [string, number, boolean]; // ❌ interface cannot directly express tuple structures
Simple summary: As long as it's not a pure object structure type, you basically need to use type to define it.
2. Extension and Inheritance: Different Syntax, Different Behavior
Both support type extension, but the syntax and underlying processing logic are completely different:
interfaceuses theextendskeyword for inheritancetypeuses intersection types&for merging
Basic Example
// interface inheritance
interface BaseUser { id: number }
interface Admin extends BaseUser { role: string }
// type intersection merging
type BaseUser = { id: number };
type Admin = BaseUser & { role: string };
Interview Deep Dive: Different Handling Logic for Same-Name Properties
This is a detail that 90% of people cannot answer:
When using
interface extends, if the types of same-name properties in the parent and child interfaces are incompatible, it will directly report an error, enforcing type consistency:interface A { value: string } // ❌ Error: Interface 'B' incorrectly extends interface 'A' interface B extends A { value: number }When using
typeintersection, same-name properties do not cause a syntax error; instead, the type is merged intonever(becausestring & numberhas no valid type):type A = { value: string }; type B = A & { value: number }; // ✅ No syntax error, but the actual type of value is never
Supplement: The two can extend each other — interface can extends an object type type, and type can also merge an interface via &.
3. Declaration Merging: interface Natively Supports It, type Completely Forbids It
This is a unique core feature of interface and the reason it is irreplaceable: Interfaces with the same name will automatically undergo declaration merging, while duplicate type definitions will directly report an error.
// ✅ interface declaration merging
interface User { id: number }
interface User { name: string }
// Final merge result is { id: number; name: string }
const user: User = { id: 1, name: 'Zhang San' }; // Works normally
// ❌ type duplicate declaration directly reports an error
type User = { id: number };
type User = { name: string };
// Error: Duplicate identifier 'User'
The core use case for declaration merging: Extending the types of third-party libraries, adding properties to global objects. For example, extending custom properties on the Window object or adding global properties to a Vue instance must rely on interface's declaration merging.
From a design philosophy perspective, interface is open, type is closed — interfaces are born to define "extensible contracts," while type aliases are just naming a fixed type.
4. Function Type Definition: Syntactic Form Differences
Although both can define function types, the expression form and applicable scenarios have clear differences:
typedefines function types more concisely, highly consistent with arrow function syntax:type FetchData = (url: string, params?: object) => Promise<any>;interfacedefines function types using "call signature" syntax:interface FetchData { (url: string, params?: object): Promise<any>; }
interface's call signature has a unique advantage: it is suitable for defining function objects with properties (functions that also have extra properties), which is very common when encapsulating libraries or defining utility function types:
interface Counter {
(): number; // Call signature: the function itself
count: number; // Property of the function
reset(): void; // Method of the function
}
5. Advanced Type Capabilities: Type Programming Exclusive to type
TypeScript's advanced type features (mapped types, conditional types, infer inference, template literal types, etc.) can all only be implemented using type; interface does not support them at all.
The utility types we use daily, such as Partial, Required, Pick, etc., are essentially implemented using type:
// Mapped types: can only be implemented with type
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
// Conditional types: can only be implemented with type
type IsString<T> = T extends string ? true : false;
If you need to do type gymnastics, write utility types, or handle complex type operations, then type is the only choice.
6. Detail Differences in Class Implementation
In scenarios where a class implements something, both can be implemented by a class, but there is a detail difference:
A class can implements an interface, and can also implements an object type type. But if the type defines a union type, the class cannot implement it:
type UserType = { id: number } | { name: string };
// ❌ Error: A class can only implement an object type or intersection of object types
class User implements UserType {}
Since interface can only be an object type, this problem does not exist, and semantically it is more aligned with the object-oriented concept of "class implements interface."
3. Practical Selection: When to Use Which?
After covering all the differences, many people still struggle: which one should I actually use when writing code?
Here is a universal set of selection principles, simple and easy to remember, and also aligned with the mainstream practices of the TS community:
Table
| Scenario | Recommended | Core Reason |
|---|---|---|
| Defining component Props, API responses, public object contracts | interface | Extensible, supports declaration merging, clear semantics |
| Primitive type aliases, union types, tuples | type | interface cannot implement these |
| Utility types like mapped types, conditional types | type | Only type supports type operations |
| Simple function type definitions | type | More concise and intuitive syntax |
| Function objects with properties, hybrid types | interface | Call signature semantics are clearer |
| Class implementation interfaces, OOP abstraction | interface | Aligns with object-oriented design semantics |
| Extending third-party libraries, global types | interface | Relies on declaration merging feature |
A simple one-sentence summary: Use interface for object contracts and extensible scenarios, use type for flexible composition and type operations.
The current mainstream trend in the TS community is: in daily business development, the usage frequency of type is getting higher and higher because it is more flexible and covers more scenarios; while interface is more often used in scenarios requiring extensibility, such as public type definitions and library type declarations.