Ao trabalhar com a interface FormData em JavaScript, onde os dados são anexados como pares chave/valor, não há uma maneira integrada de impor a segurança do tipo nas chaves que você anexa. Isso pode levar a erros de digitação, chaves ausentes e erros inesperados em tempo de execução. Mas no TypeScript, podemos resolver isso ao impor uma validação estrita das chaves.

Eu mesmo precisei dessa solução ao enviar os valores do meu formulário para uma API. Mais tarde, percebi que havia cometido vários erros de digitação em mais de um par chave/valor que estava tentando anexar à minha carga útil. Como o FormData aceita qualquer string como chave, pude passar as strings erradas e prosseguir com a solicitação da API.

Depois que isso aconteceu, busquei uma maneira de garantir que o TypeScript não permitisse esses erros.

Este artigo mostrará como tornar as chaves do FormData seguras por tipo usando TypeScript.

Pré-requisitos

Para aproveitar ao máximo este artigo, você deve ter uma compreensão básica do seguinte:

  1. programação JavaScript

  2. fundamentos do TypeScript, especialmente como interfaces, tipos e o operador keyof funcionam

  3. a interface FormData

Se você é novo no TypeScript ou FormData, recomendo dar uma olhada na documentação oficial do TypeScript e no guia do MDN sobre FormData antes de prosseguir.

Passo 1: Defina Suas Chaves Permitidas

O Método Antigo

A maneira padrão de anexar dados com FormData é fazer isso manualmente, com strings simples:

const payload = new FormData();

payload.append("id", "1122");
payload.append("name", "Clark Kent");

payload.append("agge", "36"); // Erro de digitação na chave é permitido

No trecho de código acima, você pode ver que houve um erro de digitação ao definir uma chave para age. Mas o TypeScript não sinalizará isso como um erro, e isso poderia levar a erros quando esses dados forem enviados com uma solicitação de API.

A Maneira Melhor

Em vez de digitar manualmente as chaves, defina-as em um esquema de objeto com uma interface TypeScript.

interface MyAllowedData {
    id: number;
    name: string;
    age: number;
}

Alternativamente, você pode defini-las com tipos:

type MyAllowedData = {
    id: number;
    name: string;
    age: number;
}

Você pode usar tipos ou interfaces, é apenas uma questão de preferência. Você pode descobrir mais sobre como eles diferem neste ambiente de testes oficial da documentação do TypeScript.

Em seguida, defina um tipo de união para cada chave em sua interface.

type MyFormDataKeys = keyof MyAllowedData
// isso é o mesmo que `type MyFormDataKeys = 'id' | 'name' | 'age'`

O operador keyof ajuda a criar um tipo de união das chaves de um tipo de objeto, sendo muito útil se você não quiser definir manualmente um tipo de união para um objeto maior com muitas chaves.

Passo 2: Crie uma Função Auxiliar de Adição

Agora que você definiu suas chaves estritamente tipadas, o próximo passo é criar uma função auxiliar que garante que apenas chaves válidas sejam adicionadas ao FormData.

function appendToFormData (formData: FormData, key: MyFormDataKeys, value: string) {
  formData.append(key, value);
};

A função appendToFormData recebe três argumentos. Veja como tudo funciona:

  • O primeiro argumento, formData, é uma instância do objeto FormData. É aqui que pares chave/valor serão adicionados antes de enviá-los em uma requisição API.

  • O segundo argumento, key, é o nome da chave do campo que você deseja adicionar. Seu tipo é MyFormDataKeys, o tipo de união que criamos para garantir que apenas aquelas chaves que definimos sejam adicionadas ao FormData.

  • O terceiro argumento é uma string value que representa o valor a ser adicionado com a chave.

Note que FormData só aceita os string e Blob tipos como valores em cada par chave/valor. Neste guia, estamos apenas trabalhando com valores de string – mas tenha em mente que você pode usar valores de blob para anexar arquivos a requisições de API.

Agora, vamos testar a função:

const payload = new FormData();

appendToFormData(payload, "id", "19282"); // ✅ Permitido
appendToFormData(payload, "name", "Lenny Brown"); // ✅ Permitido
appendToFormData(payload, "age", "20"); // ✅ Permitido

appendToFormData(payload, "someOtherKey", "89"); // ❌ Erro TypeScript: Argumento do tipo 'someOtherKey' não é atribuível.

Passo 3: Use a Função Auxiliar após o Envio do Formulário

Agora vamos anexar nossos campos ao FormData antes de enviá-los para uma API.

const handleSubmitForm = () => {
  const payload = new FormData();
   appendToFormData(payload, "id", "19282");
   appendToFormData(payload, "name", "Lenny Brown");
   appendToFormData(payload, "age", "20");

  // Enviar payload via API
  fetch("/api/submit", { method: "POST", body: payload });
};

Anexando Campos de um Objeto

Alternativamente, se você já tiver todo o seu payload em um objeto, pode evitar anexar cada campo um por um implementando a função assim:

const handleSubmitForm = () => {
  // todos os seus campos em um objeto
  const formValues: MyAllowedData = {
    id: 1123,
    name: 'John Doe',
    age: 56
  }
  const payload = new FormData();

  Object.entries(formValues).forEach(([key, value]) => {
    appendToFormData(payload, key as MyFormDataKeys, `${value}`); // use literais de template para passar o valor
  });

  // Enviar payload via API
  fetch("/api/submit", { method: "POST", body: payload });
};

No trecho acima, estamos usando Object.entries para iterar sobre cada par chave/valor em um objeto para que ele possa ser anexado ao objeto FormData. Note que o valor em cada par, seja ele uma string ou um número, é passado como uma string usando template literals para evitar uma incompatibilidade de tipo do TypeScript com o argumento value em nossa função auxiliar.

Conclusão

Ao aproveitar o operador keyof do TypeScript, podemos tornar o FormData.append() totalmente seguro em relação aos tipos. Essa técnica simples ajuda a evitar incompatibilidades de chaves e torna suas solicitações de API mais confiáveis.

Deixe-me saber o que você achou do artigo e fique à vontade para fazer quaisquer sugestões que você acha que poderiam melhorar minha solução.

Obrigado por ler!