JavaScriptでFormDataインターフェースを使用する際、データはキー/値のペアとして追加されますが、追加するキーの型の安全性を強制する組み込みの方法はありません。これにより、タイプミス、キーの欠落、予期しないランタイムエラーが発生する可能性があります。しかし、TypeScriptでは厳格なキーバリデーションを強制することでこれを解決できます。

私は自分自身がフォームの値をAPIに送信する際にこの解決策を必要としました。後で、ペイロードに追加しようとしている複数のキー/値ペアにいくつかの誤字があることに気付きました。FormDataはキーとして任意の文字列を受け入れるため、間違った文字列を渡してAPIリクエストを続行することができました。

これが起こった後、TypeScriptがこれらのエラーを許容しないようにする方法を探しました。

この記事では、TypeScriptを使用してFormDataのキーを型安全にする方法を紹介します。

前提条件

この記事を最大限に活用するには、以下の基本的な理解が必要です:

  1. JavaScriptプログラミング

  2. 特に、インターフェース、型、およびkeyof演算子の動作についてのTypeScriptの基礎知識

  3. FormDataインターフェース

TypeScriptやFormDataに不慣れな方は、進む前に TypeScriptの公式ドキュメントMDNのFormDataガイド を確認することをお勧めします。

ステップ1: 許可されたキーを定義する

従来の方法

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関数は3つの引数を受け取ります。以下はその動作方法です:

  • 最初の引数formDataはFormDataオブジェクトのインスタンスです。ここにキー/値のペアがAPIリクエストを送信する前に追加されます。

  • 2番目の引数keyは、追加したいフィールドのキー名です。その型はMyFormDataKeysであり、定義したキーのみがFormDataに追加されるようにするためのユニオンタイプです。

  • 3番目の引数は、キーに追加する値を表す文字列valueです。

注意してください FormDataは string および Blob タイプのみを値として受け入れます 各キー/バリューペアで。 このガイドでは、文字列値のみを扱っていますが、APIリクエストにファイルを追加するためにblob値を使用できることを忘れないでください。

さあ、関数をテストしてみましょう:

const payload = new FormData();

appendToFormData(payload, "id", "19282"); // ✅ 許可されている
appendToFormData(payload, "name", "Lenny Brown"); // ✅ 許可されている
appendToFormData(payload, "age", "20"); // ✅ 許可されている

appendToFormData(payload, "someOtherKey", "89"); // ❌ TypeScriptエラー:型'someOtherKey'の引数は割り当て可能ではありません。

ステップ3:フォーム送信後にヘルパー関数を使用する

さて、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 });
};

オブジェクトからのフィールドの追加

また、すでにオブジェクトにペイロード全体がある場合は、次のように関数を実装することで、各フィールドを1つずつ追加することを避けることができます:

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のkeyof演算子を活用することで、FormData.append()を完全に型安全にすることができます。このシンプルなテクニックは、キーミスマッチを防ぎ、APIリクエストをより信頼性の高いものにします。

記事についてのご意見をお聞かせください。また、私の解決策を改善するための提案があれば、どんなものでもご自由にお示しいただけます。

お読みいただきありがとうございました!