Executando verificação de segurança...
21

Por que você deveria evitar enums no TypeScript

Enums são uma das poucas features do TypeScript que eu recomendo evitar, e na verdade eu tenho lidado com isso de outra forma nos meus projetos.

Vou explicar o porquê, começando com:

Problema de runtime

Um enum como este:

enum UserType {
  Admin = "admin",
  Common = "common"
}

Gera código de runtime no seu bundle:

var UserType;
(function (UserType) {
    UserType["Admin"] = "admin";
    UserType["Common"] = "common";
})(UserType || (UserType = {}));

TypeScript deveria ser uma linguagem zero-runtime, e os enums quebram essa regra.

Enums numéricos são inseguros

Ok, talvez isso não seja tão grande assim, mas olha isso:

enum Status {
  Active,   // 0
  Inactive  // 1
}

const status: Status = 999; // TypeScript aceita QUALQUER número!!!

Se você reordenar os membros, todos os valores mudam. TypeScript não vai pegar isso. Pode acontecer né... E ninguém vai ver na PR.

A forma melhor que eu uso

export const UserType = {
  Admin: "admin",
  Common: "common",
} as const;

export type UserType = typeof UserType[keyof typeof UserType];

Ou ainda mais simples com arrays, dependendo do seu caso:

const USER_TYPES = ["admin", "common"] as const;
type UserType = typeof USER_TYPES[number]; // "admin" | "common"

Mesma segurança de tipos, zero código de runtime, e você pode derivar tipos diretamente dos seus dados.

Sinceramente eu prefiro a primeira opção com export const UserType.

Ah, e o Anders Hejlsberg, criador do TypeScript, já expressou que não incluiria enums se pudesse começar de novo. Então...

Use const objects ou const arrays no lugar.

Como vocês lidam com enums?

Carregando publicação patrocinada...
4

interessante essa questão, inclusive vou evitar enum daqui em diante.

uma observação: eu testei aqui e Typescript não aceita qualquer número para um tipo enum

eu colei o código do exemplo no Typescript Playground

enum Status {
  Active,   // 0
  Inactive  // 1
}

const status: Status = 999; // TypeScript aceita QUALQUER número!!!

e obtive o seguinte erro:

Type '999' is not assignable to type 'Status'.(2322)

de qualquer forma, muito válida a questão levantada e eu gostei muito da abordagem proposta poreliassoares

1
3

Geralmente esses enums nunca estão sozinhos, então eu gosto de fazer assim:

type AgentType = "client"|"supplier"|"employee"

e aí eu defino coisas relacionadas por exmeplo ao agent, e inclusive posso diferenciar por tipo de agente:

type AgentTypeConfig = {
    label: string,
    allowedIdentityTypes: IdentityType[]
    requiredIdentityTypes: IdentityType[]
}

aí eu defino:

const AGENT_TYPES: Record<AgentType, AgentTypeConfig> = {
    client: { label: "Consumidor Final", allowedIdentityTypes: ["CPF"], requiredIdentityTypes: ["CPF"] }
    // ...
}

Aí no codigo se eu precisar extrair a lista de AGENT_TYPES disponiveis, eu faço Object.keys(AGENT_TYPES) por exemplo (se for recorrente, ja deixo uma constante para isso no proprio arquivo que defino AGENT_TYPES.

1

Entendi, vc usa direto o Tipo e define o mapeamento.

Funciona bem também, e o VSCode e outras IDEs já entendem isso. Acho que o lance do enum é mais pra quando queremos ter a constante instanceada direto no código ao inves de usar as strings definidas no tipo.

1
2
2

Cara esse post me pegou com calças curtas, eu uso enum me achando o lorde da tipagem. Você está completamente certo.

Acabei de descobrir que enquanto interfaces e types somem do js final, enum continua (gerando essa IIFE).

Belo post.

2

Interessante.
Uso muito enum, mas às vezes uso class com propriedades static. Principalmente quando preciso de mais informações no objeto.

Ex.:

class UserRole {
  constructor(public slug, public name){}
  
  
  static get admin () {
     return new UserRole("admin", "Administrador do Sistema")
  }


}



Meio que funciona como enum mas tem mais informações incluídas

1

Boa. Eu também prefiro a primeira opção, só mudaria a nomenclatura, usar o sufixo Type em num objeto causa uma sensação estranha na usabilidade (UserType.Admin) na minha visão. Eu nomearia de UserRolesConst ou UserRolesDictionary.

0

Ótima informação, enum deveria ser critério de avaliação para barrar PRs.
Fui conversar com o chatGpt para entender o quanto isso afetaria o tamanho de um bundle para exemplo, segue abaixo a resposta:

📦 Quanto um enum pesa no bundle?
Um enum simples como:
export enum Status { Ativo, Inativo }
vira algo em torno de 150–300 bytes minificados no bundle final.
Parece nada, né?
Agora multiplica:
50 enums → ~10–15 KB
100 enums → ~20–30 KB
200 enums → ~40–60 KB
⚠️ isso não é tree-shakable, mesmo que você use só um valor.