Médio TypeScript

Remover opcionalidade e readonly

Enunciado

Crie um mapped type MutableRequired<T> que remova readonly e ? de todas as propriedades de T. Use os modificadores -readonly e -?.

Requisitos

  • O tipo deve se chamar MutableRequired e aceitar um parâmetro genérico T.
  • Todas as propriedades devem ser mutáveis (sem readonly).
  • Todas as propriedades devem ser obrigatórias (sem ?).
  • O tipo deve preservar os tipos originais das propriedades.

Código inicial

type MutableRequired<T> = /* complete aqui */;

// Teste:
interface Config {
  readonly host?: string;
  readonly port?: number;
}

const cfg: MutableRequired<Config> = { host: "localhost", port: 8080 };
cfg.host = "127.0.0.1"; // deve funcionar
cfg.port = 3000; // deve funcionar
// const cfg2: MutableRequired<Config> = { host: "localhost" }; // deve dar erro (port obrigatório)
Ver dica

Use -readonly [K in keyof T]-?: T[K].

Mostrar solução
type MutableRequired<T> = {
  -readonly [K in keyof T]-?: T[K];
};

// Teste:
interface Config {
  readonly host?: string;
  readonly port?: number;
}

const cfg: MutableRequired<Config> = { host: "localhost", port: 8080 };
cfg.host = "127.0.0.1"; // OK
cfg.port = 3000; // OK
// const cfg2: MutableRequired<Config> = { host: "localhost" }; // Erro: propriedade 'port' está faltando

Passo a passo

  1. Definimos MutableRequired<T> como um mapped type genérico.
  2. Usamos -readonly antes dos colchetes para remover o modificador readonly de cada propriedade.
  3. Usamos -? após [K in keyof T] para remover a opcionalidade.
  4. O tipo T[K] mantém o tipo original de cada propriedade.
  5. No teste, criamos cfg com todas as propriedades obrigatórias e mutáveis.
  6. Atribuições a cfg.host e cfg.port são permitidas.
  7. Criar cfg2 sem port gera erro de compilação.

Por que funciona

Os modificadores -readonly e -? instruem o TypeScript a remover os modificadores correspondentes durante a construção do mapped type. Isso é o oposto de readonly e ?, que os adicionam. O resultado é um tipo com todas as propriedades mutáveis e obrigatórias, equivalente a Required<{ -readonly [K in keyof T]: T[K] }>.

Erros comuns

  • Usar +readonly em vez de -readonly: +readonly adiciona readonly, não remove.
  • Colocar -? no lugar errado: { [K in keyof T]-?: T[K] } está correto; { [K in keyof T]?-: T[K] } é inválido.
  • Esquecer de remover readonly e depois tentar atribuir: se readonly não for removido, a atribuição falha.

Outra forma de resolver

Combinar tipos utilitários:

type MutableRequired<T> = Required<{ -readonly [K in keyof T]: T[K] }>;

Mas isso ainda requer um mapped type para remover readonly. A solução direta é mais clara.

Saída esperada

Nenhuma saída em tempo de execução; o código demonstra tipagem estática. As atribuições são permitidas e a falta de propriedade obrigatória gera erro de compilação.