Implementing Interfaces
The implements clause connects a class to one or more interfaces, asking the compiler to verify that the class provides everything the interface promises. Unlike extends, which copies behavior down from a base class, implements is purely a compile-time contract: it adds no code and no inheritance, it only enforces shape. A class can implement many interfaces at once, which is how TypeScript models capabilities without multiple inheritance. This page covers the implements clause, implementing several interfaces together, the relationship between implements and extends, and the subtle ways interface checking does and does not constrain a class.
The implements clause
Add implements SomeInterface after the class name. The compiler then requires the class to declare every member the interface defines, with compatible types.
interface Serializable {
serialize(): string;
}
class Document implements Serializable {
constructor(private content: string) {}
serialize(): string {
return JSON.stringify({ content: this.content });
}
}
// ❌ Error: Class 'Note' incorrectly implements 'Serializable'.
// Property 'serialize' is missing.
class Note implements Serializable {}
Document satisfies the contract by providing serialize. Note is rejected because it omits a required member. The interface is the checklist; the class must tick every box.
Implementing multiple interfaces
A class can implement any number of interfaces in a comma-separated list. It must then satisfy the union of all their members — a clean way to compose capabilities.
interface Readable {
read(): string;
}
interface Writable {
write(data: string): void;
}
class FileStore implements Readable, Writable {
private data = "";
read(): string {
return this.data;
}
write(data: string): void {
this.data = data;
}
}
FileStore is both Readable and Writable, so it is usable anywhere either interface is expected. This is TypeScript’s answer to mixing roles without multiple class inheritance.
implements vs extends
The two clauses do very different jobs and can be combined. extends inherits implementation from a class; implements only checks shape against an interface. A class may extend one class and implement many interfaces.
class Entity {
constructor(public id: string) {}
}
interface Timestamped {
createdAt: Date;
}
class Post extends Entity implements Timestamped {
createdAt = new Date();
constructor(id: string, public title: string) {
super(id);
}
}
Post inherits id from Entity (real code) and is checked against Timestamped (contract only). The order is fixed: extends comes before implements.
| Clause | Brings | Count | Effect |
|---|---|---|---|
extends (class) | Implementation + types | One | Inherits behavior |
implements (interface) | Type contract only | Many | Enforces shape |
extends (interface) | Merges interface types | Many | For interfaces, not classes |
implements does not infer types
A subtle but important rule: implements checks the class but does not give its members types from the interface. You still annotate members yourself; the clause only verifies them afterward.
interface Handler {
handle(input: string): number;
}
class MyHandler implements Handler {
// `input` is implicitly 'any' under noImplicitAny — implements
// does NOT supply the parameter type from the interface.
handle(input) {
return input.length;
}
}
Because the parameter is not annotated, it is any (an error under noImplicitAny), even though Handler.handle declares it as string. Always annotate members explicitly; treat implements as a check, not a source of inference.
Implementing interfaces with optional and readonly members
Interfaces can declare readonly and optional (?) members. A class implementing them must respect those modifiers — optional members may be omitted, and readonly ones cannot be reassigned after construction.
interface Config {
readonly env: string;
debug?: boolean;
}
class AppConfig implements Config {
readonly env: string;
debug = false; // optional member — providing it is fine
constructor(env: string) {
this.env = env;
}
}
A class can implement an interface that was declared with
typeas well — any object type alias works in animplementsclause, as long as it describes an object shape (not a union). Interfaces remain the conventional choice for class contracts.
Best Practices
- Use
implementsto enforce a public contract while keeping the class free to add extras. - Implement multiple small, focused interfaces instead of one large one (interface segregation).
- Annotate class members explicitly —
implementschecks types but does not infer them. - Combine one
extendswith severalimplementsto share code and declare capabilities. - Prefer interfaces over abstract classes when you only need a contract, not shared logic.
- Keep interface names capability-oriented (
Serializable,Comparable) for clarity. - Respect
readonlyand optional modifiers from the interface in your implementation.