Tuesday, March 11, 2025

How to Enforce Type Safety in FormData with TypeScript

Programming LanguageHow to Enforce Type Safety in FormData with TypeScript


When working with the FormData interface in JavaScript, where data is appended as key/value pairs, there’s no built-in way to enforce type safety on the keys you append. This can lead to typos, missing keys, and unexpected runtime errors. But in TypeScript, we can solve this by enforcing strict key validation.

I needed this solution myself when sending my form values to an API. I later realized that I had made several typographical errors in more than one key/value pair I was trying to append to my payload. Because FormData accepts any string as a key, I was able to pass in the wrong strings and proceed with the API request.

After this happened, I looked for a way to ensure that TypeScript doesn’t allow those errors.

This article will show you how to make FormData keys type-safe using TypeScript.

Prerequisites

To get the most out of this article, you should have a basic understanding of the following:

  1. JavaScript programming

  2. TypeScript fundamentals, especially how interfaces, types, and the keyof operator work

  3. the FormData interface

If you’re new to TypeScript or FormData, I recommend checking out TypeScript’s official documentation and MDN’s guide on FormData before proceeding.

Step 1: Define Your Allowed Keys

The Old Way

The default way of appending data with FormData is to do it manually, with plain strings:

const payload = new FormData();

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

payload.append("agge", "36"); 

In the code snippet above, you can see that there was a typo when defining a key for age. But TypeScript won’t flag it as an error, and this could lead to errors when this data is sent with an API request.

The Better Way

Instead of manually typing out the keys, define them in an object schema with a TypeScript interface.

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

Alternatively, you can define them with types:

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

You can use either types or interfaces, it’s just a matter of preference. You can find out more about how they differ in this official TypeScript documentation playground.

Next, define a union type from each key in your interface.

type MyFormDataKeys = keyof MyAllowedData

The keyof operator helps to create a union type of an object type’s keys, so it comes in really handy if you don’t want to manually define a union type for a larger object with many keys.

Step 2: Create an Append Helper Function

Now that you’ve defined your strictly-typed keys, the next step is to create a helper function that ensures only valid keys are appended to FormData.

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

The appendToFormData function takes in three arguments. Here’s how it all works:

  • The first argument, formData, is an instance of the FormData object. This is where key/value pairs will be appended before sending them in an API request.

  • The second argument, key, is the key name of the field you want to append. Its type is of MyFormDataKeys, the union type we created to ensure only those keys we defined are appended to FormData.

  • The third argument is a string value which represents the value to be appended with the key.

Note that FormData only accepts the string and Blob types as values in each key/value pair. In this guide, we’re only working with string values – but keep in mind that you can use blob values for appending files to API requests.

Now, let’s test out the function:

const payload = new FormData();

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

appendToFormData(payload, "someOtherKey", "89"); 

Step 3: Use the Helper Function after Form Submission

Now let’s append our fields to FormData before sending them to an API.

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

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

Appending Fields from an Object

Alternatively, if you already have your entire payload in an object, you can avoid appending each field one by one by implementing the function like this:

const handleSubmitForm = () => {
  
  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}`); 
  });

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

In the snippet above, we’re using Object.entries to iterate over each key/value pair in an object so it can be appended to the FormData object. Note that the value in each pair, whether it’s a string or a number, is passed as a string using template literals to prevent a TypeScript type mismatch from the value argument in our helper function.

Conclusion

By leveraging TypeScript’s keyof operator, we can make FormData.append() fully type-safe. This simple technique helps prevent key mismatches and makes your API requests more reliable.

Let me know your thoughts about the article, and feel free to make any suggestions you think could improve my solution.

Thanks for reading!

Check out our other content

Check out other tags:

Most Popular Articles