在JavaScript中使用FormData接口时,数据被追加为键/值对,但没有内置的方法来强制执行对追加的键进行类型安全验证。这可能导致拼写错误、缺少键和意外的运行时错误。但是在TypeScript中,我们可以通过强制执行严格的键验证来解决这个问题。

当我将表单值发送到API时,我自己也需要这个解决方案。后来我意识到,在我试图追加到负载中的多个键/值对中,我犯了几个拼写错误。因为FormData接受任何字符串作为键,所以我能够传入错误的字符串并继续进行API请求。

在此情况发生后,我寻找了一种方法来确保TypeScript不允许出现这些错误。

本文将向您展示如何使用TypeScript使FormData的键类型安全

先决条件

为了充分理解本文内容,您应该对以下内容有基本的了解:

  1. JavaScript编程

  2. TypeScript基础知识,特别是接口、类型以及keyof运算符的工作原理

  3. FormData接口

如果你是 TypeScript 或者 FormData 的新手,我建议在继续之前查看TypeScript 的官方文档MDN 上关于 FormData 的指南

第一步:定义允许的键

老方法

使用 FormData 追加数据的默认方式是手动进行,使用普通字符串:

const payload = new FormData();

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

payload.append("agge", "36"); // 键中允许存在拼写错误

在上面的代码片段中,你可以看到在定义age键时存在拼写错误。但 TypeScript 不会将其标记为错误,这可能会导致在发送 API 请求时出现错误。

更好的方法

不要手动键入键,而是使用 TypeScript 接口在对象模式中定义它们。

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

或者,你可以使用类型进行定义:

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

你可以使用类型或接口,只是个人喜好的问题。你可以在这个官方 TypeScript 文档示例中了解它们的区别。

接下来,从接口中的每个键定义一个联合类型。

type MyFormDataKeys = keyof MyAllowedData
// 这与 `type MyFormDataKeys = 'id' | 'name' | 'age'` 相同

keyof运算符有助于创建对象类型的键的联合类型,因此,如果您不想手动为具有许多键的较大对象定义联合类型,它非常实用。

步骤2:创建一个追加助手函数

现在您已经定义了严格类型的键,下一步是创建一个助手函数,确保只有有效的键被追加到FormData

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

appendToFormData函数接受三个参数。以下是它的工作原理:

  • 第一个参数formDataFormData对象的一个实例。这是在将它们发送到API请求之前追加键/值对的地方。

  • 第二个参数key是您想要追加的字段的键名。它的类型是MyFormDataKeys,我们创建的联合类型,以确保只有我们定义的那些键被追加到FormData

  • 第三个参数是一个字符串value,表示要与键一起追加的值。

请注意,FormData只接受 string Blob 类型作为每个键/值对的值。在本指南中,我们只使用字符串值 – 但请记住,您可以使用Blob值将文件附加到API请求中。

现在,让我们测试一下这个函数:

const payload = new FormData();

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

appendToFormData(payload, "someOtherKey", "89"); // ❌ TypeScript错误:无法分配'type'的参数。

第三步:在表单提交后使用助手函数

现在,让我们在将字段发送到API之前将它们附加到FormData中。

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

  // 通过API发送载荷
  fetch("/api/submit", { method: "POST", body: payload });
};

从对象附加字段

或者,如果您已经将整个载荷保存在对象中,您可以通过以下方式实现函数而避免逐个附加每个字段:

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}`); // 使用模板文字传递值
  });

  // 通过API发送载荷
  fetch("/api/submit", { method: "POST", body: payload });
};

在上面的代码片段中,我们使用Object.entries来迭代对象中的每个键/值对,以便将其附加到FormData对象。请注意,每个键/值对中的值(无论是字符串还是数字)都被传递为字符串,使用模板文字来防止我们辅助函数中value参数的TypeScript类型不匹配。

结论

通过利用TypeScript的keyof运算符,我们可以使FormData.append()完全类型安全。这种简单的技术有助于防止键不匹配,并使您的API请求更可靠。

请告诉我您对这篇文章的想法,也欢迎提出任何您认为可以改进我的解决方案的建议。

感谢阅读!