author face photo

Ensuring Type-Safe FormData in TypeScript

Published April 2, 2025

How 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:

  • JavaScript programming
  • TypeScript fundamentals, especially how interfaces, types, and the keyof operator work
  • The FormData interface

Information: 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

const payload = new FormData(); payload.append("id", "1122"); payload.append("name", "Clark Kent"); payload.append("agge", "36"); // Typo in key is allowed

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

interface MyAllowedData { id: number; name: string; age: number; } type MyFormDataKeys = keyof MyAllowedData; // this is the same as `type MyFormDataKeys = 'id' | 'name' | 'age'`

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

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

Tip: The appendToFormData function takes in three arguments: an instance of FormData, a key (validated by TypeScript), and a string value to be appended.

const payload = new FormData(); appendToFormData(payload, "id", "19282"); // ✅ Allowed appendToFormData(payload, "name", "Lenny Brown"); // ✅ Allowed appendToFormData(payload, "age", "20"); // ✅ Allowed appendToFormData(payload, "someOtherKey", "89"); // ❌ TypeScript Error

Step 3: Use the Helper Function after Form Submission

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

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 }); };

Warning: Ensure that all values are correctly converted to strings to prevent TypeScript errors.

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!


Dmytro Notes