本セッションの登壇者
セッション動画(YouTubeチャンネル登録もお願いします。)
こんにちは、Angularチャンネルの発表をさせていただきますlacolacoです。よろしくお願いします。今回お話するのは、Angularの新しい型安全なフォームAPIについてです。
簡単に自己紹介します。私はClassi株式会社で働いているlacolacoと申します。AngularのGoogle Developers Expertとして活動していたり、Angular日本ユーザー会の代表をしていたりします。詳しいプロフィールはg.dev/lacoから見てください。
今日お話するAngular Forms APIですが、このAPI自体はAngularの最初からずっとあります。どういったものかというと、 フォームを扱うためのAPIなのですが、大きく2つ、ひとつはテンプレート駆動フォーム、もうひとつはリアクティブフォームというものが従来からあります。
テンプレート駆動フォームはHTMLテンプレートだけで完結できるのでお手軽ですが、複雑な制御は苦手という特徴があります。
もうひとつのリアクティブフォームというのは、TypeScriptで書くフォームモデルをベースにフォームを組んでいます。細かく制御できますが、少し複雑というのが特徴のAPIです。
今回、次のバージョンのAngular 14においてリアクティブフォームに、大きな改善が入ります。それが型安全になるということです。
リアクティブフォームAPIの特徴と課題
もともとのリアクティブフォームAPIがどういうAPIだったかというと、図1のようにフォームをFormGroupというようなkey-valueのオブジェクトでモデル化したり、 ひとつひとつのフォームの入力をフォームコントロールというデータモデルで表現したりして、その値の変更、つまりvalueが変わったことをvalueChangesのようなストリームでイベントを取得したり、あるいは動的、ユーザー入力ではなくプログラム的に値を変更するsetValueができたりといった具合に、データモデルを情報源としてフォームを扱う、そういったAPIになっていました。
ただし、このAPIには大きな課題がずっとあって、これまでこのAPIにはanyだったり、nullだったり、そういったものがつきまとっていて、開発者が型をしっかり付けたいときには困るAPIでした。これはバージョンの最初からずっとそうだったんですが、バージョン14においてようやく型が付くことになりました。
とくにStrictlyということで、TypeScriptの最新のstrictモードにまでしっかり対応した厳密な型定義ができるようになっています。大きくはnullやanyがなくなったこと、フォームモデルの型がイミュータブルになっていることです。けっこう大きい変更なんですが、この変更はバージョン14の自動アップグレード時のマイグレーションで、既存コードも破壊的に変更されることなく移行される見込みです。
今日は、これがどんなAPIになるのかをさっと見ていきたいと思います。
nullとundefined - 厳密な型定義が可能に
単純に何をするかというと、フォームコントロールにジェネリクスが追加されて、そのコントロールがどの型のインプット入力を受け取るのかということを指定できます。何も書かなければ、初期値の型から自動的に推論されますし、明示的に書くこともできます。
ただしこの値は、与えたジェネリクスそのままの型が手に入るわけではありません。図4のように、与えた型がTだとしたら、そのTに加えてnullとundefinedも取得する際には考慮されます。
なぜこれがTではないのでしょうか?
ひとつはリセットという機能があるからです。Web標準のHTMLフォームと同様に、このフォームモデルにもリセット機能があります。リセットすると初期値、すなわちデフォルト値に戻されるのですが、このデフォルト値はもともとnullです。なので、nullになり得るということはnullableである必要がある。この部分で初期値は「初期値をデフォルト値とします」というオプションがあるので、それをするとリセットしても型が変わらないので、この場合はT、つまりnullがなくなってTというものになります。
これがinitialValueIsDefaultというオプションです。これはバージョン14で追加されます。
これを毎回、全部付けるのは大変なので、簡単にNon Nullableなフォームを作れるFormBuilderも追加されています。
図7のように、初期値をデフォルト値として型が変わらないようにNon Nullableなフォームを作れるようになっています。
もうひとつ、undefinedですが、これはFormControlのvalueがつねにPartialであるからです。なぜかというと、フォームはdisabledにすることができます。disabledとなったフォームからはvalueが取れません。undefinedになります。これはHTMLタグのformタグの挙動と同じになっています。
なので、disabled状態によらない未加工の値を取るgetRawValueを使うと、Tそのものの型を取得できます。
このような違いがありますが、これはプログラムがどういった処理をするかによって使い分けることになります。
参考資料
ちょっと短い紹介となりましたが、これはバージョン14の機能なので、まだ正式なドキュメントはありません。もっと知りたい方には、現時点の参考資料として、以下を紹介しておきます。
ひとつは、次期バージョンのドキュメントで、簡単なまとめを見られます。
もうひとつは、このAPIがデザインされるに至った議論「[Complete] RFC: Strictly Typed Reactive Forms #44513」のRFCs(リクエストフォーコメント)を見ると、どうしてこのAPIが実装されたのかというモチベーションから、何が考慮されていて、何がまだ実装されてないのか、何が計画されているものなのか、といった過去の話や未来の話、そのどちらも見られるようになっています。このあたりを深く知りたい方は、ぜひRFCも読んでみていただきたいと思います。
では、発表を終わります。ありがとうございました。