⌨️

TypeScriptでHTMLFormElementを使うときはFormData APIを使うと文字列でValueを取得できる

2023/11/22に公開2

TypeScriptだと文字列でインデックスのアクセスがうまく行かないケースがある

TypeScriptでフォームの値を取得する際にはHTMLFormElement型が利用されます。しかしながら、この型には文字列インデックスのシグネチャがないため、以下のようにするとエラーが発生します。

const formElement = document.querySelector('form') as HTMLFormElement;
const value = formElement['inputName']; // Error!
// 特に文字列シグネチャを配列か何かで持っていて、走査したいとき困る
// JSではドット記法による動的な参照は許されていないため
// 動的なプロパティ参照にはブラケット記法を使わなくてはならない
const form = event.currentTarget as HTMLFormElement;
const entry = names.map(id => {
	const elem = form.elements;
	return elem[name].value // Error!
})

エラーメッセージは次のようになります。

Element implicitly has an 'any' type because index expression is not of type 'number'.

これは文字列インデックスにアクセスできないことが原因です。これはHTMLFormElement型定義の制限なので、避けようがありません。

FormData APIを使おう

しかしながら、FormDataコンストラクターを使うことでこの問題は簡単に回避できます。

const formElement = document.querySelector('form');

const formData = new FormData(formElement); 

formData.get('inputName'); // 文字列インデックスでアクセス OK

FormDataオブジェクトはgetメソッドを介して文字列インデックスに対応しているので、こちらを利用することで文字列キーによる値参照が可能です。

結論として、フォームデータを取得する場合はなるべくFormData APIを利用することをおすすめします。ネイティブDOM APIよりも扱いやすく、名前付き要素へのアクセスにも柔軟に対応できます。

Discussion

nap5nap5

調べてみると、decode-formdataというものもあるようでした。ちょっとデモで使ってみました。

https://codesandbox.io/p/devbox/back-demo-wy32w8?file=%2Fsrc%2FApp.tsx%3A1%2C1

おるとろおるとろ

コメントありがとうございます。decode-formdata のモジュールを見てきたのですが、こちらは内部的にDom APIを使っているシンプルなラッパーのようですね。

https://github.com/fabian-hiller/decode-formdata/blob/main/src/decode.ts#L20-L109

このモジュールでも、Dom APIを使った結果、複雑な実装になっていると言えると思います。今回の記事ではそれをしなくて済むという話になるかなと思いますので、比較対象、あるいは別の解決策の提示としては不適切に感じます。