Introdução
As interfaces são uma das pedras fundamentais do Typescript. Elas descrevem formas que um objeto deve ter — um contrato que qualquer valor implemente aquela interface precisa respeitar.
Types vs Interfaces
Essa é a dúvida mais comum entre desenvolvedores TypeScript. As duas construções parecem fazer a mesma coisa na maioria dos casos, mas têm diferenças importantes.
A regra prática é:
Objetos públicos, contratos de classe → interface
Unions, primitivos, mapped types, composições → type
Ambas descrevem a forma de um objeto:
type Usuario = {
id: number;
nome: string;
};
interface Usuario {
id: number;
nome: string;
}
Na prática, para objetos simples, são intercambiáveis (trocado, substituído).
Diferença 1 — Declaration Merging
Interfaces podem ser declaradas múltiplas vezes e o TypeScript mescla automaticamente:
interface Configuracao {
host: string;
}
interface Configuracao {
porta: number;
}
// TypeScript mescla as duas:
// { host: string; porta: number }
const config: Configuracao = {
host: "localhost",
porta: 5432
};
Com type, isso gera erro:
type Configuracao = { host: string };
type Configuracao = { porta: number }; // ❌ identificador duplicado
Declaration merging é especialmente útil para estender libs externas sem modificar o código delas.
Diferença 2 — Tipos compostos
type consegue representar qualquer coisa, interface só representa objetos:
// só type consegue fazer isso:
type ID = string | number;
type Status = "ativo" | "inativo";
type Nullable<T> = T | null;
type Par<T> = [T, T];
Diferença 3 — Extends vs Intersecção
// interface usa extends
interface Animal { nome: string }
interface Cachorro extends Animal { raca: string }
// type usa &
type Animal = { nome: string }
type Cachorro = Animal & { raca: string }
Funcionalmente equivalentes, mas extends em interface gera mensagens de erro mais claras.
Declaração de Interfaces
Interface declaration é a sintaxe formal de declarar uma interface — mas vai além de propriedades simples. Interfaces suportam vários tipos de membros.
Propriedades
interface Produto {
id: number; // obrigatória
descricao?: string; // opcional
readonly codigo: string; // somente leitura
}
readonly impede atribuição após a criação do objeto.
Métodos
interface Repositorio<T> {
buscarPorId(id: string): Promise<T>;
salvar(entidade: T): Promise<T>;
deletar(id: string): Promise<void>;
listar(): Promise<T[]>;
}
Call Signatures
interface Validador {
(valor: string): boolean;
}
const validarCNPJ: Validador = (cnpj) => cnpj.length === 14;
Implementação em classes
interface Transmissor {
transmitir(payload: unknown): Promise<Protocolo>;
consultar(protocolo: string): Promise<Status>;
}
class TransmissorReinf implements Transmissor {
async transmitir(payload: unknown): Promise<Protocolo> {
// implementação
}
async consultar(protocolo: string): Promise<Status> {
// implementação
}
}
Se a classe não implementar algum método da interface, o TypeScript acusa erro em tempo de compilação.
Extendendo interfaces
Interfaces podem ser estendidas para criar hierarquias de contratos sem duplicar código.
Extends simples
interface EntidadeBase {
id: string;
criadoEm: Date;
atualizadoEm: Date;
}
interface Tenant extends EntidadeBase {
cnpj: string;
razaoSocial: string;
}
interface Evento extends EntidadeBase {
tipo: string;
competencia: string;
}
Extends múltiplo
Uma interface pode estender várias ao mesmo tempo:
interface ComAuditoria {
criadoPor: string;
atualizadoPor: string;
}
interface ComSoftDelete {
deletadoEm?: Date;
ativo: boolean;
}
interface EntidadeCompleta extends ComAuditoria, ComSoftDelete {
id: string;
}
// EntidadeCompleta tem tudo:
// id, criadoPor, atualizadoPor, deletadoEm, ativo
Sobrescrevendo propriedades
Você pode sobrescrever uma propriedade herdada, mas o novo tipo precisa ser compatível com o original:
interface Base {
id: string | number;
}
interface Derivada extends Base {
id: string; // ✅ string é subconjunto de string | number
}
interface Invalida extends Base {
id: boolean; // ❌ boolean não é compatível com string | number
}
Caso prático — sistema fiscal
interface EntidadeBase {
id: string;
criadoEm: Date;
atualizadoEm: Date;
ativo: boolean;
}
interface EventoFiscal extends EntidadeBase {
tenantId: string;
competencia: string;
status: "pendente" | "processando" | "concluido" | "erro";
}
interface EventoEFDReinf extends EventoFiscal {
codigoEvento: string;
cnpjContribuinte: string;
}
interface EventoEFinanceira extends EventoFiscal {
rubrica: string;
tipoMovimento: string;
}
Tipos Híbridos
Hybrid types são interfaces que descrevem um valor que é simultaneamente uma função e um objeto. Parece incomum, mas existe em várias libs JavaScript.
Hybrid types são raramente necessários em código novo — são mais úteis para tipar libs JavaScript existentes que foram escritas antes do TypeScript existir. Em código novo, prefira separar a função do objeto explicitamente.
Em JavaScript, funções são objetos — então uma função pode ter propriedades:
function contador() { ... }
contador.total = 0; // função com propriedade
contador.resetar = () => { ... }; // função com método
Interfaces conseguem descrever exatamente isso.
interface Contador {
(): number; // call signature — é uma função
total: number; // propriedade
resetar(): void; // método
}
function criarContador(): Contador {
let count = 0;
const contador = function(): number {
return ++count;
} as Contador;
contador.total = 0;
contador.resetar = () => { count = 0; };
return contador;
}
const c = criarContador();
c(); // chama como função → 1
c(); // → 2
c.total; // acessa propriedade
c.resetar(); // chama método
Top comments (0)