TL;DR

  • TypeScript の型は実行時に消えるので、そのままでは実行時のバリデーションに使えない。
  • 外部ソースから型を生成する、実行時の型ライブラリで型を定義する、型から実行時の値を生成する、の 3 つの方法がある。

内容

TypeScript の型は実行時に消去されるので、実行時にはアクセスできない。そのため、実行時のバリデーションの実装が煩雑になりやすい。これを避けるには、次のいずれかの方法を取る。

外部ソースから型を生成する

OpenAPI や GraphQL Schema のような外部ソースがあれば、そこから型を生成する(項目42)。OpenAPI なら json-schema-to-typescript が使える。

信頼できる情報源が新たに増えない(Single Source of Truth)のが利点で、追加のビルドステップが必要になるのが欠点である。

実行時の型ライブラリを使って型を定義する

TypeScript では静的な型から実行時の値は作れないが、逆は typeof を使えばできる。

const val = { postId: "123", title: "First", body: "That is all" }
type ValType = typeof val
//   ^? type ValType = { postId: string; title: string; body: string; }

アプリケーションでこれを行うには、通常 Zod などのライブラリを使う。TypeScript の型では表現しにくいルール(有効なメールアドレスや整数など)を表現でき、追加のビルドステップも要らない。

一方で、Zod の定義(z.object)と TypeScript の定義(interface)が混在し、Zod の使い方を別に学ぶ必要もある。また、実行時の型は伝染する。ある型が別の型を参照していれば、参照先の型も実行時の型に作り変えなければならない。

型から実行時の値を生成する

TypeScript の型から JSON Schema を生成する typescript-json-schema のようなツールを使う方法もある。型を TypeScript で定義できるのが利点で、新しいライブラリとビルドステップが必要になる(CI に組み込む)のが欠点である。

キーワード