TypeScript: Classes e interfaces do TypeScript
Última atualização: 2026-08-26
Classes e interfaces são os dois pilares da programação orientada a objetos no TypeScript — as classes fornecem implementações, enquanto as interfaces definem contratos. Quando uma classe implementa uma interface, o TypeScript garante que a classe atenda a todos os requisitos especificados pela interface.
1. Uma classe implementa uma interface (implementa)
(1) Sintaxe básica
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) A importância da implementação de interfaces
Uma interface é um “contrato” — quando uma classe implementa uma interface, ela se compromete a dizer: “Vou fornecer todas as propriedades e métodos exigidos pela 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) Implementação de múltiplas interfaces
Uma classe pode implementar várias interfaces — separadas por vírgulas:
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)
▶ Exemplo: Definindo uma API unificada por meio de interfaces
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
Saída:
Charlie
2
2. Classes que herdam de interfaces
Uma característica única do TypeScript: as interfaces podem herdar definições de membros de classes (herdando apenas as declarações de tipo, e não as implementações):
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. Classes abstratas (abstract)
Uma classe abstrata é uma “classe incompleta” — ela fornece uma implementação parcial, e as subclasses devem completar o restante:
(1) Sintaxe básica
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) Classes abstratas x interfaces
| Característica | Classe abstrata | Interface |
|---|---|---|
| Método de implementação | Pode ter uma implementação específica | Não deve ter nenhuma implementação |
| Construtor | Pode ter | Não pode ter |
| Herança múltipla | Apenas herança única | Herança múltipla permitida (extends) |
| Modificadores de acesso | Suporta public/private/protected | As propriedades são públicas por padrão |
| Instanciação | Não pode ser instanciado diretamente | Não pode ser instanciado (tipo puro) |
| Existe em tempo de execução | Sim (compilado em uma classe JS) | Não (declaração de tipo pura) |
(3) Quando usar classes abstratas
- Várias subclasses compartilham a mesma implementação parcial (reutilização de código)
- Os membros protegidos devem permitir que as subclasses tenham acesso a eles
- Requer lógica de construtor
(4) Quando usar interfaces
- Basta definir “o que ele pode fazer” (descrição das capacidades)
- Uma classe pode precisar implementar várias funcionalidades (implementação de várias interfaces)
- Não é necessário compartilhar o código de implementação
- É necessária uma instrução MERGE
▶ Exemplo: Implementação do padrão Template Method com uma classe abstrata
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"
Saída:
HELLO WORLD
[Reversal] dlrow olleh
4. Usando classes como tipos
Uma definição de classe é, por si só, um tipo — você pode usar o nome da classe como o tipo de uma variável:
(1) O conceito de classe
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) Tipo de classe versus o operador typeof
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
▶ Exemplo: Implementando múltiplas interfaces em uma única classe
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}'
Saída:
Task #1: Learn TypeScript [pending]
{"id":1,"title":"Learn TypeScript","done":false}
❓ Perguntas Frequentes
P: Qual é a diferença entre
implementseextends? R:implementssignifica “implementa uma interface” — uma classe se compromete a fornecer todos os membros exigidos pela interface, mas não herda nenhum código de implementação.extendssignifica “herda de uma classe” — uma subclasse herda todo o código de implementação da superclasse e pode sobrescrevê-lo. Uma classe podeimplementsvárias interfaces, mas só podeextendsuma superclasse.
P: O que devo usar — uma classe abstrata ou uma interface? R: Uma regra simples: se várias classes precisarem compartilhar código de implementação, use uma classe abstrata; se você precisar apenas definir um contrato especificando “o que ela pode fazer”, use uma interface. É possível combinar as duas — uma interface define as capacidades, e uma classe abstrata fornece uma implementação parcial.
P: Uma classe que implementa uma interface pode ter membros adicionais? R: Sim. Uma classe que implementa uma interface precisa ter apenas “pelo menos os membros especificados pela interface”; membros adicionais são perfeitamente válidos. Por exemplo,
interface Printable { print(): void }eclass Document implements Printable { print() {...}; save() {...} }— a classeDocumentpossui um método adicionalsave, mas isso é perfeitamente válido.
P: Uma interface pode herdar de uma classe abstrata? R: Sim.
interface ISelect extends AbstractControl {}Isso é válido — a interface herdará todas as assinaturas dos membros da classe abstrata (incluindo as dos membros privados). No entanto, as classes que implementam essa interface devem fornecer suas próprias implementações para todos os membros, inclusive os privados.
📖 Resumo
implementsexige que uma classe implemente todos os membros de uma interface — o TypeScript verifica em tempo de compilação se esse requisito foi atendido- Uma classe pode implementar várias interfaces, separadas por vírgulas.
- Uma interface pode estender uma classe — herdando apenas a assinatura de tipo, e não a implementação
- Uma classe abstrata fornece uma implementação parcial; as subclasses devem implementar os métodos abstratos restantes.
- As classes abstratas são adequadas para compartilhar código de implementação, enquanto as interfaces são adequadas para definir contratos de capacidade; ambas podem ser usadas em conjunto.
- Uma definição de classe é, por si só, um tipo — ela pode ser usada como anotação de tipo para variáveis.
📝 Exercícios
- Problema básico (Dificuldade ⭐): Defina a interface
Loggable(com um métodolog) e faça com que as classesUsereProducta implementem, respectivamente. Crie instâncias e chame o métodolog. - Problema avançado (Dificuldade ⭐⭐): Defina uma classe abstrata
Vehicle(com uma propriedadebrand, um método abstratostarte um método concretodescribe) e, em seguida, crie as subclassesCareBikeque implementam o métodostart, respectivamente. Escreva uma função que receba um parâmetro do tipoVehiclee chame o métododescribe. - Desafio (Dificuldade: ⭐⭐⭐): Projete uma interface
SortableCollection<T>(com uma propriedadelength, um métodocomparee um métodoswap) e faça com que a classeNumberCollectiona implemente. Em seguida, implemente o algoritmo de classificação por bolhas na classe abstrataSorter(usando os métodos da interface), de modo que as subclasses precisem apenas fornecer implementações concretas decompareeswap.