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

TYPESCRIPT
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”:

TYPESCRIPT
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:

TYPESCRIPT
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

TYPESCRIPT
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
▶ Experimente

Saída:

TEXT 📖 Somente leitura
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):

TYPESCRIPT
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());
  }
}
💡 Objetivo: As classes que herdam de interfaces são utilizadas principalmente em hierarquias complexas de classes para descrever tipos que “possuem a estrutura de uma determinada classe e, ao mesmo tempo, apresentam capacidades adicionais”. Elas não são comumente utilizadas no desenvolvimento cotidiano.



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

TYPESCRIPT
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

(4) Quando usar interfaces

▶ Exemplo: Implementação do padrão Template Method com uma classe abstrata

TYPESCRIPT
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"
▶ Experimente

Saída:

TEXT 📖 Somente leitura
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

TYPESCRIPT
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
⚠️ Observação: A seção de tipos de uma classe contém apenas propriedades de instância e assinaturas de métodos. Ao atribuir um objeto compatível com estrutura, os métodos não são incluídos — apenas as propriedades são correspondidas. Para acessar os métodos, você deve criar uma instância usando new Point().

(2) Tipo de classe versus o operador typeof

TYPESCRIPT
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

TYPESCRIPT
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}'
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Task #1: Learn TypeScript [pending]
{"id":1,"title":"Learn TypeScript","done":false}

❓ Perguntas Frequentes

P: Qual é a diferença entre implements e extends? R: implements significa “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. extends significa “herda de uma classe” — uma subclasse herda todo o código de implementação da superclasse e pode sobrescrevê-lo. Uma classe pode implements várias interfaces, mas só pode extends uma 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 } e class Document implements Printable { print() {...}; save() {...} } — a classe Document possui um método adicional save, 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

📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Defina a interface Loggable (com um método log) e faça com que as classes User e Product a implementem, respectivamente. Crie instâncias e chame o método log.
  2. Problema avançado (Dificuldade ⭐⭐): Defina uma classe abstrata Vehicle (com uma propriedade brand, um método abstrato start e um método concreto describe) e, em seguida, crie as subclasses Car e Bike que implementam o método start, respectivamente. Escreva uma função que receba um parâmetro do tipo Vehicle e chame o método describe.
  3. Desafio (Dificuldade: ⭐⭐⭐): Projete uma interface SortableCollection<T> (com uma propriedade length, um método compare e um método swap) e faça com que a classe NumberCollection a implemente. Em seguida, implemente o algoritmo de classificação por bolhas na classe abstrata Sorter (usando os métodos da interface), de modo que as subclasses precisem apenas fornecer implementações concretas de compare e swap.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%