Naming

Tudo que vale para nomes em JavaScript continua valendo aqui: camelCase, verbo que declara a intenção, nome tirado do negócio em vez do tipo técnico. O TypeScript acrescenta três categorias de nome que o JavaScript não tem, porque não tem tipos: a interface (contrato que descreve a forma de um objeto), o type alias (apelido dado a um tipo) e o generic parameter (parâmetro genérico, a variável de tipo que aparece na assinatura). Esta página cobre as três.

Conceitos fundamentais

ConceitoO que é
interface (contrato de objeto)Forma de objeto extensível e implementável; usada para shapes públicos
type alias (apelido de tipo)type X = ...: apelido para union, intersection, mapped ou primitivo
generic parameter (parâmetro genérico)Variável de tipo na assinatura (T, K); ganha nome longo quando o domínio pede
PascalCase (estilo Pascal)Convenção para tipos, classes e enums (UserService, OrderStatus)
camelCase (estilo camelo)Convenção para variáveis e funções (fetchUser, currentOrder)
domain term (termo de domínio)Palavra que pertence ao negócio (invoice, subscriber); evita rótulos técnicos (object, entity)
boolean prefix (prefixo booleano)is, has, can, should: torna o nome legível como pergunta (isActive)
suffix convention (convenção de sufixo)Props, Result, Options: sinaliza papel sem repetir tipo

O prefixo I fica fora do nome da interface

O I de IUser vem do Java e do C#, onde a convenção nasceu. Em TypeScript ele não acrescenta informação: quem lê interface User já tem a palavra interface na frente dos olhos. O prefixo alonga o nome em todo lugar onde o tipo aparece, e a assinatura fica mais longa sem ficar mais clara.

❌ Ruim: prefixo I em todas as interfaces
interface IUser { /* ... */ }
interface IOrderRepository { /* ... */ }
interface IPaymentGateway { /* ... */ }

function findUser(repo: IOrderRepository): IUser { /* ... */ }
✅ Bom: PascalCase direto, sem prefixo
interface User { /* ... */ }
interface OrderRepository { /* ... */ }
interface PaymentGateway { /* ... */ }

function findUser(repo: OrderRepository): User { /* ... */ }

O sufixo mostra o papel do tipo na arquitetura

Alguns tipos existem para cumprir um papel na estrutura do sistema, e o sufixo é o lugar de dizer qual. Service, Repository, Handler, Config e Options são reconhecíveis: quem bate o olho no nome já sabe onde a peça encaixa. Manager e Helper falham nesse teste, porque servem para qualquer coisa.

❌ Ruim: nomes vagos, prefixos desnecessários e sufixos sem papel claro
interface IUserRepository { /* ... */ }       // prefixo I desnecessário
interface AbstractBaseHandler { /* ... */ }   // "Abstract" e "Base" não dizem o papel
interface UserManager { /* ... */ }           // Manager não expressa contrato claro
interface UserHelper { /* ... */ }            // Helper não diz o que faz

interface IOrderService {
  handleOrder(data: unknown): Promise<unknown>;
}
✅ Bom: o sufixo mostra onde a peça encaixa
interface UserService {
  findById(id: string): Promise<User>;
  create(data: CreateUserInput): Promise<User>;
}

interface UserRepository {
  findById(id: string): Promise<User | null>;
  save(user: User): Promise<void>;
}

interface AuthConfig {
  secret: string;
  audience: string;
  expiresIn: string;
}

O apelido de tipo diz o que o valor representa

Uma assinatura com três string seguidas obriga quem chama a conferir a documentação para saber a ordem. Um apelido como type UserId = string resolve a leitura: createOrder(userId: UserId, productId: ProductId) mostra qual valor vai em qual posição.

O apelido não muda nada em runtime, e vale saber o que ele não faz: o compilador continua aceitando um ProductId no lugar de um UserId, porque para ele os dois são string. O ganho está na assinatura que o leitor entende de primeira.

❌ Ruim: três parâmetros string seguidos, sem dizer qual é qual
function createOrder(userId: string, productId: string, currency: string): Promise<string> { /* ... */ }
// quem chama não sabe a ordem dos parâmetros: todos são string
✅ Bom: o apelido nomeia o que cada parâmetro carrega
type UserId = string;
type ProductId = string;
type Currency = "BRL" | "USD" | "EUR";
type OrderId = string;

function createOrder(userId: UserId, productId: ProductId, currency: Currency): Promise<OrderId> { /* ... */ }

Com dois genéricos na assinatura, uma letra deixa de bastar

T sozinho funciona: existe um só parâmetro de tipo, e o leitor não tem com o que confundir. A partir do segundo, T e U viram adivinhação, porque nada na letra diz qual deles é a entrada e qual é o resultado. Nomes como TItem, TKey, TValue e TResult respondem isso na própria assinatura.

❌ Ruim: T e U não dizem qual é a entrada e qual é a saída
function mapCollection<T, U>(items: T[], transform: (item: T) => U): U[] { /* ... */ }

function groupBy<T, U>(items: T[], keySelector: (item: T) => U): Map<U, T[]> { /* ... */ }
✅ Bom: nomes que expressam o papel do parâmetro
function mapCollection<TItem, TResult>(items: TItem[], transform: (item: TItem) => TResult): TResult[] { /* ... */ }

function groupBy<TItem, TKey>(items: TItem[], keySelector: (item: TItem) => TKey): Map<TKey, TItem[]> { /* ... */ }

Prefira um objeto as const ao enum nativo

O enum é a única construção de tipo do TypeScript que sobrevive à compilação. interface e type desaparecem quando o código vira JavaScript; o enum vira um objeto de verdade e viaja no arquivo final que o navegador baixa. Como ele é um objeto que o build não consegue provar que ninguém usa, o tree-shaking (a remoção do código não utilizado durante o build) costuma deixá-lo passar.

Um objeto marcado com as const, mais o union type derivado dele, dá o mesmo autocompletar e a mesma checagem em compilação, aceita a string literal direto na chamada, e some quando o TypeScript compila.

❌ Ruim: o enum nativo sobrevive à compilação e vai para o arquivo final
enum OrderStatus {
  Pending = "pending",
  Approved = "approved",
  Cancelled = "cancelled",
}

function updateStatus(status: OrderStatus) { /* ... */ }

updateStatus(OrderStatus.Approved); // obrigado a usar o enum: não aceita a string direta
✅ Bom: um objeto as const, e o union type derivado dele
const ORDER_STATUS = {
  pending: "pending",
  approved: "approved",
  cancelled: "cancelled",
} as const;

type OrderStatus = typeof ORDER_STATUS[keyof typeof ORDER_STATUS];
// "pending" | "approved" | "cancelled"

function updateStatus(status: OrderStatus) { /* ... */ }

updateStatus("approved"); // string literal aceita diretamente
updateStatus(ORDER_STATUS.approved); // ou via objeto: ambos funcionam

DoDocs v3.7.0 · Desenvolvido por @thiagocajadev · Baseado no trabalho de pmndrs/docs · Poimandres.