interface
| Since: | TypeScript 1.0(2014) |
|---|
Defines the "shape" of an object — that is, what properties it has. You can use an interface as a contract that a class must implement, or as the type for function arguments and return values.
Syntax
interface InterfaceName {
propertyName: type;
propertyName?: type; // Optional property (can be omitted)
readonly propertyName: type; // Read-only property
}
interface NewName extends ExistingName {
additionalProperty: type;
}
Syntax Reference
| Syntax | Description |
|---|---|
| interface Name { ... } | Creates a type definition for an object. Specifies property names, types, and modifiers. |
| propertyName?: type | An optional property. If omitted, its value is undefined. |
| readonly propertyName: type | A read-only property whose value cannot be changed after initialization. |
| extends | Inherits and extends another interface. You can extend multiple interfaces at once. |
| Declaration merging | If you define an interface with the same name more than once, the definitions are automatically merged into one. |
| implements | Declares that a class implements an interface. The class is required to implement all properties and methods defined in the interface. |
Sample Code
interface User {
id: number;
name: string;
email?: string; // Optional property.
skill?: string; // Optional property.
readonly createdAt: Date; // Read-only property.
}
const user: User = {
id: 1,
name: "member_1",
skill: "data_analysis",
createdAt: new Date(),
};
console.log(user.name); // Outputs "member_1".
// user.createdAt = new Date(); // Error: cannot reassign a readonly property.
// Extend an interface to add more properties.
interface AdminUser extends User {
role: "admin" | "superadmin";
permissions: string[];
}
const admin: AdminUser = {
id: 2,
name: "Admin",
createdAt: new Date(),
role: "admin",
permissions: ["read", "write", "delete"],
};
console.log(admin.role); // Outputs "admin".
// Implement interfaces in a class.
interface Printable {
print(): void;
}
interface Serializable {
serialize(): string;
}
class Document implements Printable, Serializable {
constructor(private content: string) {}
print(): void {
console.log(this.content);
}
serialize(): string {
return JSON.stringify({ content: this.content });
}
}
const doc = new Document("Hello, TypeScript!");
doc.print(); // Outputs "Hello, TypeScript!".
// Declaration merging: interfaces with the same name are merged automatically.
interface Config {
host: string;
}
interface Config {
port: number; // Merged with the previous Config interface automatically.
}
const config: Config = { host: "localhost", port: 3000 };
console.log(`${config.host}:${config.port}`); // Outputs "localhost:3000".
Running the above produces the following output:
npx ts-node ts_interface.ts member_1 admin Hello, TypeScript! localhost:3000
Overview
interface is the primary way to define the "shape" of an object in TypeScript. By requiring objects to have specific properties and methods, you can guarantee the structure of values passed as function arguments and verify that a class has implemented all required members.
A distinctive feature of interfaces is declaration merging: if you define an interface with the same name in multiple places, TypeScript automatically combines them into a single definition. This is especially useful when you want to extend a library's type definitions from outside (type augmentation).
Both interfaces and type aliases can define object types, but interfaces work well with classes and support inheritance and declaration merging. Using interfaces for object structure definitions is the common convention.
Why Both interface and type Exist
TypeScript provides two ways to describe the shape of an object: interface and type aliases. Both features exist for historical reasons, and their roles differ in subtle ways.
Historical Background
Originally, only interface existed (TypeScript 1.0, 2012). It was designed as a direct port of the "interface" concept from Java and C# — a traditional object-oriented programming (OOP) tool for defining the contract a class must fulfill.
Later, the need arose to handle union types (types that accept multiple alternatives, such as string | number), aliases for primitive types, tuples, conditional types, and more. The type alias was added as a mechanism for giving a name to anything. Because the existing interface could not be removed, both features remained side by side.
The TypeScript design team has publicly acknowledged that both exist for historical reasons, and there are no plans to merge them. As a result, users need to understand the role of each and choose accordingly.
An Analogy from C
For developers familiar with C, think of interface as similar to struct (a named structure) and type as similar to typedef (a type alias).
| C | TypeScript | Common Trait |
|---|---|---|
struct | interface | Declares a structure (a collection of properties). Can be redeclared with the same name to merge definitions. |
typedef | type | Gives an alias to anything. Redeclaration with the same name is not allowed. |
Just as C's struct Name { ... } "defines a named structure" while typedef "attaches a short alias to an existing type," the two TypeScript features follow a similar division of responsibility.
Declaration Merging (Available Only with interface)
When you declare an interface with the same name in multiple places, TypeScript automatically merges them into one. This feature, called declaration merging, is useful when you want to extend the types of an external library, and it is something type cannot do.
A Typical Real-World Example — Extending Express Types
Consider a case where you want to attach user information extracted by an authentication middleware to the request object (Request) in Express (a Node.js web framework). You cannot rewrite Express's own type definitions, but with declaration merging you can add fields from your own project.
// Declare this in a file such as 'types/express/index.d.ts'.
import "express";
declare module "express" {
interface Request {
userId?: string; // User ID extracted from the JWT after authentication.
organizationId?: string; // Organization ID extracted from the JWT after authentication.
}
}
// From here on, req.userId / req.organizationId can be used type-safely in any route handler.
import express from "express";
const app = express();
app.get("/profile", (req, res) => {
console.log(req.userId); // Type-checks pass because userId exists on interface Request.
console.log(req.organizationId);
res.json({userId: req.userId});
});
Trying to achieve the same thing with type would require rewriting Express's own type definitions, which is practically impossible. Extending library types is the exclusive domain of interface.
How Declaration Merging Works
Declaration merging applies not only to properties but also to method signatures. However, if the same property appears with conflicting types in two declarations, a compile error is raised.
// OK: adding different properties.
interface Config {
host: string;
}
interface Config {
port: number;
}
const config: Config = {host: "localhost", port: 3000}; // Both are required.
// NG: same property name with conflicting types.
interface BadConfig {
value: string;
}
interface BadConfig {
value: number; // Error: type conflicts with string.
}
When to Choose interface
As a practical guideline, choose interface in the following situations.
- Extending library types (declaration merging is required). Use this when you need to add your own fields to external library types such as Express, React, or Fastify.
- Defining contracts for classes to implement (works well with the
implementskeyword). Use this when designing with an interface-driven, OOP-style architecture. - Defining plain object structures (convention). For types like
ChatDataorDashboardDatathat represent "the shape of data," the TypeScript community convention leans towardinterface.
When in doubt, choosing interface covers about 80% of situations without issue. Switch to type only when you need something that interface cannot express — such as union types, primitive aliases, tuples, or conditional types. For details, see type (type alias).
If you find any errors or copyright issues, please contact us.