Ensuring Type-Safe FormData in TypeScript
Published April 2, 2025How 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