Como Usar Interface no TypeScript
Interface no TypeScript: shape de objeto, extends e quando preferir type alias.
Por que isso é importante
Interface no TypeScript descreve a shape de objeto e permite extends/merge. Use para contratos públicos; prefira type quando precisar de união.
O Que É Interface no TypeScript
Interface é um contrato. Ela descreve a forma de um objeto: quais propriedades ele tem, quais são os tipos dessas propriedades e quais são obrigatórias ou opcionais. Pensa nela como um molde: todo objeto que segue esse molde é compatível.
Quando você pede dados de uma API, espera receber um objeto com campos específicos. Nome, email, idade. Se a API retorna algo diferente, seu código quebra. Interface garante que o formato tá certo em tempo de compilação. Nada de surpresas em runtime.
TypeScript usa tipagem estrutural, não nominal. Isso quer dizer que se um objeto tem todas as propriedades que a interface exige, ele é compatível. Não precisa declarar explicitamente "esse objeto implementa essa interface". Se a forma bate, serve. Quem vem de Java ou C# estranha no começo, mas depois percebe como isso é liberador.
Interfaces também podem ser estendidas (uma interface herda de outra), implementadas por classes e combinadas por declaration merging. Galera confunde interface com type, mas cada um tem seu espaço. Aqui vamos focar 100% em interfaces e como tirar o máximo delas.
Um ponto forte das interfaces: elas são abertas. Isso significa que se você declarar a mesma interface duas vezes, o TypeScript combina as propriedades das duas. Isso é declaration merging e é muito usado em bibliotecas que precisam ser estendidas por quem consome. Type alias não suporta isso.
Como Usar Interface Passo a Passo
Do básico ao avançado, cada passo adiciona uma camada de poder às suas interfaces. Segue a ordem que vai te deixar produtivo mais rápido.
Exemplos Práticos com Interface
Vamos ver interfaces em cenários reais que você encontra no dia a dia de desenvolvimento. Cada exemplo é algo que você pode copiar e adaptar pro seu projeto.
Interface Básica: Modelando Dados
// Interface para um produto
interface Produto {
id: number;
nome: string;
preco: number;
descricao: string;
emEstoque: boolean;
}
// Objeto que segue a interface
const notebook: Produto = {
id: 1,
nome: "MacBook Pro",
preco: 12999.90,
descricao: "Laptop profissional Apple",
emEstoque: true,
};
// Isso dá erro - falta o campo descricao:
// const mouse: Produto = {
// id: 2,
// nome: "Magic Mouse",
// preco: 699.90,
// emEstoque: true,
// };
Percebe como o compilador pega o campo faltante na hora? Sem interface, esse erro só apareceria quando alguma parte do código tentasse acessar mouse.descricao e recebesse undefined. Com interface, nem compila sem estar completo.
Propriedades Opcionais e Readonly
interface Usuario {
readonly id: number; // não muda depois de criado
nome: string;
email: string;
telefone?: string; // opcional
avatar?: string; // opcional
readonly criadoEm: Date; // não muda
}
const user: Usuario = {
id: 1,
nome: "Maria",
email: "maria@email.com",
criadoEm: new Date(),
};
// Funciona - campo opcional não precisa existir
console.log(user.telefone); // undefined
// Erro! Campo readonly
// user.id = 2; // Cannot assign to 'id' because it is a read-only property
// Funciona - campo normal
user.nome = "Maria Silva";
readonly é subestimado. Dados que vêm de banco de dados (id, criadoEm) não devem mudar no frontend. Marcar como readonly previne mutações acidentais. É uma camada extra de proteção que custa zero linhas de código a mais.
Estendendo Interfaces (Herança)
// Interface base
interface Pessoa {
nome: string;
idade: number;
}
// Estende Pessoa com campos extras
interface Funcionario extends Pessoa {
cargo: string;
salario: number;
departamento: string;
}
// Estende Funcionario (herança em cadeia)
interface Gerente extends Funcionario {
subordinados: Funcionario[];
nivel: "junior" | "senior" | "diretor";
}
// Múltipla herança
interface Endereco {
rua: string;
cidade: string;
cep: string;
}
interface FuncionarioCompleto extends Funcionario, Endereco {
// tem tudo de Funcionario + Endereco
dataAdmissao: Date;
}
const dev: FuncionarioCompleto = {
nome: "Carlos",
idade: 30,
cargo: "Dev Senior",
salario: 15000,
departamento: "Tech",
rua: "Rua das Flores, 123",
cidade: "São Paulo",
cep: "01000-000",
dataAdmissao: new Date("2023-01-15"),
};
Herança de interface evita duplicação. Em vez de repetir nome e idade em Funcionario, você estende Pessoa. Se um dia Pessoa ganha um campo novo, Funcionario e Gerente herdam automaticamente. Isso escala: projetos com 50+ interfaces ficam organizados porque a hierarquia é clara.
Interface com Métodos e Implements
// Interface com métodos
interface Repositorio {
buscarPorId(id: number): Promise<Usuario | null>;
salvar(usuario: Usuario): Promise<void>;
deletar(id: number): Promise<boolean>;
listar(): Promise<Usuario[]>;
}
// Classe que implementa a interface
class UsuarioRepositorio implements Repositorio {
private usuarios: Usuario[] = [];
async buscarPorId(id: number): Promise<Usuario | null> {
return this.usuarios.find(u => u.id === id) || null;
}
async salvar(usuario: Usuario): Promise<void> {
this.usuarios.push(usuario);
}
async deletar(id: number): Promise<boolean> {
const index = this.usuarios.findIndex(u => u.id === id);
if (index === -1) return false;
this.usuarios.splice(index, 1);
return true;
}
async listar(): Promise<Usuario[]> {
return [...this.usuarios];
}
}
Esse pattern de Repository é clássico em Clean Architecture. A interface define o contrato, a classe implementa. Se amanhã você trocar de banco de dados, só muda a classe. Todo o resto do código depende da interface, não da implementação. É inversão de dependência na prática.
Index Signatures: Propriedades Dinâmicas
// Quando não sabe os nomes das propriedades antecipadamente
interface Dicionario {
[chave: string]: string;
}
const traducoes: Dicionario = {
hello: "olá",
world: "mundo",
code: "código",
};
// Combinar propriedades fixas com dinâmicas
interface ConfigApp {
versao: string;
nome: string;
[chave: string]: string; // aceita qualquer propriedade extra
}
const config: ConfigApp = {
versao: "1.0.0",
nome: "MeuApp",
ambiente: "producao",
regiao: "us-east-1",
};
Index signatures são úteis quando os nomes das propriedades são dinâmicos. Dicionários de tradução, configurações com chaves variáveis, caches indexados por string. Use com cuidado: o TypeScript não consegue garantir que uma chave específica existe, só que se existir, será do tipo declarado.
Erros Comuns com Interface
Armadilhas que pegam até devs experientes
Confundir com type: interface e type resolvem problemas parecidos, mas interface é melhor pra objetos e classes, enquanto type é melhor pra unions e tipos primitivos. Use interface como padrão pra objetos e troque pra type só quando precisar de union ou intersection.
Esquecer que TypeScript é estrutural: se dois objetos têm a mesma forma, são compatíveis mesmo sem declarar a interface. Isso é poderoso mas pode surpreender quem vem de Java ou C#. Não existe implements obrigatório pra objetos literais.
Propriedades extras em literais: ao atribuir um objeto literal direto, TypeScript barra propriedades extras (excess property checking). Mas ao passar por variável intermediária, aceita. Cuidado com essa inconsistência aparente, ela é proposital.
Não usar readonly quando deveria: dados que vêm de API e não devem ser alterados no frontend merecem readonly. Previne mutações acidentais que geram bugs difíceis de rastrear. Custa nada adicionar.
Interfaces gigantes: se uma interface tem mais de 10 propriedades, considere quebrá-la em interfaces menores e usar extends. Interfaces compostas são mais legíveis, testáveis e reutilizáveis.
Checklist de Interface no TypeScript
Construa Projetos com Interfaces Reais
Interface em tutorial é uma coisa. Interface num projeto real com 50 endpoints de API, autenticação, pagamentos e deploy é outra completamente diferente. No CrazyStack, você modela dados reais com interfaces que fazem sentido em produção.
Node.js, React, TypeScript, banco de dados, testes. Tudo tipado, tudo organizado. Você sai do curso sabendo estruturar qualquer projeto profissional com confiança.
Continue lendo
Como Usar Type Alias no TypeScript
Type alias, union types, intersection types e literal types na prática.
Diferença entre Type e Interface no TypeScript
Entenda quando usar type e quando usar interface no TypeScript.
Como Tipar Objetos no TypeScript
A tipar objetos complexos com nested types e index signatures.
Generics no TypeScript
Tipos reutilizáveis