Pular para o conteúdo
TypeScript

Mapped Types no TypeScript:

Domine Mapped Types no TypeScript. Syntax [K in keyof T], modifiers +/-, key remapping e template literal types, tudo com exemplos práticos.

O Que São Mapped Types

Mapped Types iteram sobre as chaves de um tipo e criam um novo tipo transformando cada propriedade. A syntax básica é: { [K in keyof T]: TipoNovo }. É como um loop for, mas pra tipos.

O keyof T pega todas as chaves de T como union type. O in itera sobre cada membro desse union. Pra cada chave K, você define o tipo do valor. Pode manter o original com T[K], transformar, ou trocar completamente.

Modifiers são o toque final. Você pode adicionar (+) ou remover (-) readonly e ? (opcional) de cada propriedade. É assim que Partial adiciona ? em tudo e Required remove.

Com key remapping (usando as), dá pra renomear as chaves durante o mapeamento. Combine com template literal types e você consegue criar coisas como getters e setters automáticos pra cada propriedade. Sem escrever uma linha a mais.

Como Usar Mapped Types Passo a Passo

Vamos construir a compreensão camada por camada. Cada passo abre possibilidades novas.

  1. Passo 1 - Syntax básica: Escreva type MeuTipo = { [K in keyof T]: T[K] }. Isso cria uma cópia exata do tipo T. Parece inútil, mas é a base de tudo.
  2. Passo 2 - Transformar valores: Troque T[K] pelo tipo que quiser. { [K in keyof T]: string } transforma todas as propriedades em string.
  3. Passo 3 - Adicionar modifier ?: Use { [K in keyof T]?: T[K] } pra tornar tudo opcional. É exatamente o que Partial faz.
  4. Passo 4 - Remover modifier com -: Use { [K in keyof T]-?: T[K] } pra tornar tudo obrigatório. O -? remove o opcional. É o Required.
  5. Passo 5 - Modifier readonly: Use { readonly [K in keyof T]: T[K] } pra tornar tudo readonly. E { -readonly [K in keyof T]: T[K] } pra remover readonly.
  6. Passo 6 - Key remapping com as: Use { [K in keyof T as NovoNome]: T[K] } pra renomear chaves. Combine com template literals pra criar padrões como get${K}.

Exemplos Práticos de Mapped Types

Do básico ao avançado, com código que você aplica agora.

Recriando Utility Types com Mapped Types

// Tipo base
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// Recriando Partial: adiciona ? em tudo
type MeuPartial<T> = {
  [K in keyof T]?: T[K];
};

type PartialUser = MeuPartial<User>;
// { id?: number; name?: string; email?: string; age?: number }

// Recriando Required: remove ? de tudo
type MeuRequired<T> = {
  [K in keyof T]-?: T[K];
};

// Recriando Readonly: adiciona readonly em tudo
type MeuReadonly<T> = {
  readonly [K in keyof T]: T[K];
};

type ReadonlyUser = MeuReadonly<User>;
// { readonly id: number; readonly name: string; ... }

// Removendo readonly: usa -readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

Transformando Valores das Propriedades

// Transformar todos os valores em boolean
type Flags<T> = {
  [K in keyof T]: boolean;
};

type UserFlags = Flags<User>;
// { id: boolean; name: boolean; email: boolean; age: boolean }

// Envolver todos os valores em Promise
type Async<T> = {
  [K in keyof T]: Promise<T[K]>;
};

type AsyncUser = Async<User>;
// { id: Promise<number>; name: Promise<string>; ... }

// Tornar todos os valores nullable
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

type NullableUser = Nullable<User>;
// { id: number | null; name: string | null; ... }

// Envolver valores em getter functions
type Getters<T> = {
  [K in keyof T]: () => T[K];
};

type UserGetters = Getters<User>;
// { id: () => number; name: () => string; ... }

Key Remapping com as

// Key remapping: renomear chaves com 'as'
// Criar getters automaticos: getName, getEmail, etc.
type AutoGetters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserAutoGetters = AutoGetters<User>;
// {
//   getId: () => number;
//   getName: () => string;
//   getEmail: () => string;
//   getAge: () => number;
// }

// Criar setters automaticos
type AutoSetters<T> = {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};

type UserAutoSetters = AutoSetters<User>;
// {
//   setId: (value: number) => void;
//   setName: (value: string) => void;
//   ...
// }

// Prefixar todas as chaves com on (eventos)
type EventHandlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void;
};

type UserEvents = EventHandlers<User>;
// { onId: (value: number) => void; onName: (value: string) => void; ... }

Filtrando Propriedades com Key Remapping

// Filtrar propriedades: remover chaves retornando never no as
interface MixedObj {
  name: string;
  age: number;
  active: boolean;
  tags: string[];
  score: number;
}

// Pegar so propriedades do tipo string
type StringProps<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

type
// { name: string }

// Pegar so propriedades do tipo number
type NumberProps<T> = {
  [K in keyof T as T[K] extends number ? K : never]: T[K];
};

type
// { age: number; score: number }

// Recriando Pick com mapped type
type MeuPick<T, Keys extends keyof T> = {
  [K in Keys]: T[K];
};

type PickedUser = MeuPick<User, 'name' | 'email'>;
// { name: string; email: string }

// Recriando Omit com mapped type + key remapping
type MeuOmit<T, Keys extends keyof T> = {
  [K in keyof T as K extends Keys ? never : K]: T[K];
};

type OmittedUser = MeuOmit<User, 'id'>;
// { name: string; email: string; age: number }

Template Literal Types em Mapped Types

// Template literal types combinados com mapped types

// Criar tipo de CSS properties
type CSSSize = 'sm' | 'md' | 'lg' | 'xl';
type CSSProp = 'padding' | 'margin' | 'gap';

type CSSClasses = {
  [S in CSSSize as `${CSSProp}-${S}`]: string;
};
// { 'padding-sm': string; 'padding-md': string; 'margin-sm': string; ... }

// Criar API de estado com update handlers
interface FormData {
  username: string;
  password: string;
  remember: boolean;
}

type FormHandlers<T> = {
  [K in keyof T as `handle${Capitalize<string & K>}Change`]: (
    value: T[K]
  ) => void;
};

type FormAPI = FormData & FormHandlers<FormData>;
// {
//   username: string;
//   password: string;
//   remember: boolean;
//   handleUsernameChange: (value: string) => void;
//   handlePasswordChange: (value: string) => void;
//   handleRememberChange: (value: boolean) => void;
// }

Mapped types são como um for loop pra tipos. Depois que você entende a syntax, começa a ver oportunidades de uso em todo lugar: formulários, APIs, estado, configurações.

Erros Comuns com Mapped Types

Armadilhas que travam a compilação

Esquecer o & string no key remapping com template literals: quando usa Capitalize, o K pode não ser string (pode ser number ou symbol). Use Capitalize pra forçar que K seja string.

Confundir keyof com valores: keyof T retorna as CHAVES do tipo, não os valores. Se T é { name: string; age: number }, keyof T é 'name' | 'age', não string | number.

Modifier - sem +: escrever -? remove o opcional, mas escrever só ? adiciona. O + é implícito: +? é o mesmo que ?. Já pra remover, o - é obrigatório.

Mapped types recursivos sem limite: se o tipo mapeado referencia a si mesmo sem condição de parada, o compilador entra em loop. Sempre use conditional type pra definir quando parar.

Tentar mapear unions primitivos: mapped types só funcionam com tipos de objeto. Pra transformar unions de primitivos, use conditional types. { [K in keyof string]: ... } não faz o que você espera.

Checklist de Mapped Types

  • Syntax [K in keyof T] usada corretamente
  • Modifiers ? e readonly aplicados ou removidos com - quando necessário
  • Key remapping com as pra renomear chaves
  • Template literal types combinados com Capitalize, Uppercase, etc.
  • Filtro de propriedades com never no remapping
  • Entende que keyof retorna chaves, não valores
  • Usa string & K quando combina com template literals
  • Mapped types não aplicados em unions primitivos

Crie Tipos Avançados na Prática

Mapped types são a ferramenta que te coloca no nível avançado de TypeScript. No CrazyStack, você usa mapped types pra criar DTOs automáticos, validações tipadas e APIs type-safe. Tudo num projeto real com Node.js e React do zero ao deploy.

Se você quer parar de copiar tipos manualmente e começar a gerar tipagem automática, esse é o caminho mais direto.

Perguntas frequentes

O Que São Mapped Types

Mapped Types iteram sobre as chaves de um tipo e criam um novo tipo transformando cada propriedade. A syntax básica é: { [K in keyof T]: TipoNovo }. É como um loop for, mas pra tipos. O keyof T pega todas as chaves de T como union type. O in itera sobre cada membro desse union. Pra cada chave K, você define o tipo do valor. Pode manter o original com T[K], transformar, ou trocar completamente. Modifiers são o toque final. Você pode adicionar (+) ou remover (-) readonly e ? (opcional) de cada propriedade. É assim que Partial adiciona ? em tudo e Required remove.

Como Usar Mapped Types Passo a Passo

Vamos construir a compreensão camada por camada. Cada passo abre possibilidades novas.