TypeScript: TypeScript Classes and Interfaces
Last updated: 2026-08-26
Classes and interfaces are the two pillars of object-oriented programming in TypeScript—classes provide implementations, while interfaces define contracts. When a class implements an interface, TypeScript ensures that the class satisfies all the requirements specified by the interface.
1. A class implements an interface (implements)
(1) Basic Syntax
interface Printable {
toString(): string;
}
class User implements Printable {
constructor(public name: string, public age: number) {}
toString(): string {
return `${this.name},${this.age} years old`;
}
}
let user = new User("Charlie", 20);
console.log(user.toString()); // "Charlie,20 years old"
(2) The Significance of Implementing Interfaces
An interface is a "contract"—when a class implements an interface, it promises, "I will provide all the properties and methods required by the interface":
interface Animal {
name: string;
speak(): string;
move(distance: number): void;
}
class Dog implements Animal {
constructor(public name: string) {}
speak(): string {
return "Woof!";
}
move(distance: number): void {
console.log(`${this.name} Moved ${distance}m`);
}
}
// ❌ Members that do not meet the interface requirements will result in an error.
// class Cat implements Animal {
// constructor(public name: string) {}
// // Missing speak and move → Compilation Error
// }
(3) Multi-interface Implementation
A class can implement multiple interfaces—separated by commas:
interface Serializable {
serialize(): string;
}
interface Comparable {
compareTo(other: this): number;
}
class Score implements Serializable, Comparable {
constructor(public value: number) {}
serialize(): string {
return JSON.stringify({ value: this.value });
}
compareTo(other: Score): number {
return this.value - other.value;
}
}
let s1 = new Score(90);
let s2 = new Score(85);
console.log(s1.serialize()); // '{"value":90}'
console.log(s1.compareTo(s2)); // 5(Positive Number Display s1 > s2)
▶ Example: Defining a Unified API Using Interfaces
Output:
Charlie
2
interface Repository<T> {
findById(id: number): T | null;
findAll(): T[];
save(entity: T): void;
delete(id: number): boolean;
}
interface User {
id: number;
name: string;
email: string;
}
class UserRepository implements Repository<User> {
private users: User[] = [];
findById(id: number): User | null {
return this.users.find(u => u.id === id) ?? null;
}
findAll(): User[] {
return [...this.users];
}
save(entity: User): void {
let index = this.users.findIndex(u => u.id === entity.id);
if (index >= 0) {
this.users[index] = entity; // Update
} else {
this.users.push(entity); // New
}
}
delete(id: number): boolean {
let len = this.users.length;
this.users = this.users.filter(u => u.id !== id);
return this.users.length < len;
}
}
let repo = new UserRepository();
repo.save({ id: 1, name: "Charlie", email: "xiao@example.com" });
repo.save({ id: 2, name: "Diana", email: "hong@example.com" });
console.log(repo.findById(1)?.name); // "Charlie"
console.log(repo.findAll().length); // 2
Output:
Charlie
2
2. Classes That Inherit from Interfaces
A unique feature of TypeScript—interfaces can inherit member definitions from classes (inheriting only the type declarations, not the implementations):
class Control {
private state: string = "active";
protected getState(): string {
return this.state;
}
}
// Interface-Inherited Classes——Inherit only the type signature
interface Selectable extends Control {
select(): void;
}
// When a class implements an interface, it must provide all members.
class Dropdown implements Selectable {
private state: string = "active"; // You must implement it yourself. private Members
protected getState(): string {
return this.state;
}
select(): void {
console.log("Selected:" + this.getState());
}
}
3. Abstract Classes (abstract)
An abstract class is a "half-finished class"—it provides a partial implementation, and subclasses must complete the rest:
(1) Basic Syntax
abstract class Shape {
// Abstract Methods——Not implemented,Subclasses must implement
abstract getArea(): number;
abstract getPerimeter(): number;
// Specific Methods——Implemented,Direct Inheritance by Subclasses
describe(): string {
return `Area:${this.getArea().toFixed(2)},Perimeter:${this.getPerimeter().toFixed(2)}`;
}
}
class Circle extends Shape {
constructor(public radius: number) {
super();
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(public width: number, public height: number) {
super();
}
getArea(): number {
return this.width * this.height;
}
getPerimeter(): number {
return 2 * (this.width + this.height);
}
}
// ❌ Abstract classes cannot be instantiated directly.
// let shape = new Shape();
let circle = new Circle(5);
let rect = new Rectangle(4, 6);
console.log(circle.describe()); // "Area:78.54,Perimeter:31.42"
console.log(rect.describe()); // "Area:24.00,Perimeter:20.00"
(2) Abstract Classes vs. Interfaces
| Feature | Abstract Class | Interface |
|---|---|---|
| Implementation Method | May have a specific implementation | Must not have any implementation |
| Constructor | Can have | Cannot have |
| Multiple inheritance | Single inheritance only | Multiple inheritance allowed (extends) |
| Access Modifiers | Supports public/private/protected | Properties are public by default |
| Instantiation | Cannot be instantiated directly | Cannot be instantiated (pure type) |
| Exists at runtime | Yes (compiled to a JS class) | No (pure type declaration) |
(3) When to Use Abstract Classes
- Multiple subclasses share the same partial implementation (code reuse)
- Protected members are required to allow subclasses to access them
- Requires constructor logic
(4) When to Use Interfaces
- You only need to define "what it can do" (capability description)
- A class may need to implement multiple capabilities (implementation of multiple interfaces)
- No need to share the implementation code
- A merge statement is required
▶ Example: Implementing the Template Method Pattern with an Abstract Class
Output:
HELLO WORLD
[Reversal] dlrow olleh
abstract class DataParser {
// Template Method——Define the Algorithm Framework
parse(input: string): string {
let raw = this.read(input);
let validated = this.validate(raw);
let transformed = this.transform(validated);
return this.format(transformed);
}
// Specific Methods——Shared by all subclasses
private read(input: string): string {
return input.trim();
}
// Abstract Methods——Each subclass implements it on its own
protected abstract validate(data: string): string;
protected abstract transform(data: string): string;
// Hook Method——Subclasses may choose to override this method
protected format(data: string): string {
return data;
}
}
class UpperParser extends DataParser {
protected validate(data: string): string {
if (data.length === 0) throw new Error("Input is empty");
return data;
}
protected transform(data: string): string {
return data.toUpperCase();
}
}
class ReverseParser extends DataParser {
protected validate(data: string): string {
return data;
}
protected transform(data: string): string {
return data.split("").reverse().join("");
}
protected format(data: string): string {
return `[Reversal] ${data}`;
}
}
let upper = new UpperParser();
let reverse = new ReverseParser();
console.log(upper.parse(" hello world ")); // "HELLO WORLD"
console.log(reverse.parse(" hello world ")); // "[Reversal] dlrow olleh"
Output:
HELLO WORLD
[Reversal] dlrow olleh
4. Using Classes as Types
A class definition is itself a type—you can use the class name as the type of a variable:
(1) The Concept of a Class
class Point {
constructor(public x: number, public y: number) {}
distanceTo(other: Point): number {
return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2);
}
}
// Class as a Type——Accept Point Instances or structurally compatible objects
let p1: Point = new Point(1, 2);
let p2: Point = { x: 3, y: 4 }; // ✅ Structural Compatibility(But does not include methods!)
console.log(p1.distanceTo(p2)); // ✅ p1 has distanceTo method
// p2.distanceTo(p1); // ❌ p2 Only x and y,There is no way
new Point().
(2) Class Type vs. the typeof Operator
class Factory {
static create(): Factory {
return new Factory();
}
constructor(public name: string) {}
}
// Factory Type——Instance Type
let instance: Factory = new Factory("Products");
// typeof Factory Type——The class itself(Constructor Types)
let FactoryClass: typeof Factory = Factory;
let another = FactoryClass.create(); // ✅ Calling a Static Method
▶ Example: Implementing Multiple Interfaces in a Single Class
interface Loggable {
log(): string;
}
interface Serializable {
serialize(): string;
}
class Task implements Loggable, Serializable {
constructor(
public id: number,
public title: string,
public done: boolean = false
) {}
log(): string {
return `Task #${this.id}: ${this.title} [${this.done ? "done" : "pending"}]`;
}
serialize(): string {
return JSON.stringify({ id: this.id, title: this.title, done: this.done });
}
}
let task = new Task(1, "Learn TypeScript");
console.log(task.log()); // "Task #1: Learn TypeScript [pending]"
console.log(task.serialize()); // '{"id":1,"title":"Learn TypeScript","done":false}'
Output:
Task #1: Learn TypeScript [pending]
{"id":1,"title":"Learn TypeScript","done":false}
❓ FAQ
implements and extends?implements means "implements an interface"—a class promises to provide all the members required by the interface, but does not inherit any implementation code. extends means "inherits from a class"—a subclass inherits all the implementation code from the superclass and can override it. A class can implements multiple interfaces, but can only extends one superclass.interface Printable { print(): void } and class Document implements Printable { print() {...}; save() {...} }—the Document class has an additional save method, but this is perfectly valid.interface ISelect extends AbstractControl {} This is valid—the interface will inherit all member signatures from the abstract class (including those of private members). However, classes that implement this interface must provide their own implementations for all members, including private ones.📖 Summary
implementsrequires a class to implement all members of an interface—TypeScript checks at compile time to ensure this requirement is met- A class can implement multiple interfaces, separated by commas.
- An interface can extend a class—inheriting only the type signature, not the implementation
- An abstract class provides a partial implementation; subclasses must implement the remaining abstract methods.
- Abstract classes are suitable for sharing implementation code, while interfaces are suitable for defining capability contracts; the two can be used in combination.
- A class definition is itself a type—it can be used as a type annotation for variables.
📝 Exercises
- Basic Problem (Difficulty ⭐): Define the
Loggableinterface (with alogmethod), and have theUserandProductclasses implement it, respectively. Create instances and call thelogmethod. - Advanced Problem (Difficulty ⭐⭐): Define an abstract class
Vehicle(with abrandproperty, an abstractstartmethod, and a concretedescribemethod), then create subclassesCarandBikethat implement thestartmethod, respectively. Write a function that takes a parameter of typeVehicleand calls thedescribemethod. - Challenge (Difficulty: ⭐⭐⭐): Design a
SortableCollection<T>interface (with alengthproperty, acomparemethod, and aswapmethod), and have theNumberCollectionclass implement it. Then, implement the bubble sort algorithm in theSorterabstract class (using the interface methods), so that subclasses only need to provide concrete implementations ofcompareandswap.