Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Codegen vs additionalProperties: When Free-Form Maps Break Typed Clients

2026年10月30日に「Dev.to」が公開したITニュース「Codegen vs additionalProperties: When Free-Form Maps Break Typed Clients」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

OpenAPIで任意のキーを持つデータ(マップ)を定義する際、`additionalProperties`に値の型を明示しないと、生成されるコードが型安全性を失う場合がある。値の型をきちんと指定し、生成されたコードが期待通りの型になっているか確認することで、安全に扱えるクライアントが作れる。

ITニュース解説

API開発において、OpenAPIスキーマはAPIのデータ構造や動作を定義する設計図として非常に重要だ。このスキーマからクライアントコードを自動生成することは、開発効率を高める上で欠かせない手法である。しかし、このコード生成の過程で、特に「自由形式マップ」と呼ばれる柔軟なデータ構造を扱う際に、型安全性が失われるという落とし穴が存在する。

自由形式マップとは、プロパティのキー(名前)が事前に全て決まっているわけではなく、実行時に動的に追加される可能性のあるデータ構造を指す。例えば、ユーザーが追加するメタデータ、地域ごとに異なるローカライゼーション文字列、または機能フラグの設定値など、スキーマが全てのキーを列挙できない場合に利用される。OpenAPIスキーマでは、このような自由形式マップを表現するためにadditionalPropertiesというキーワードを使用する。これは「スキーマで明示的に定義されていないプロパティでも受け入れる」という意味合いを持つ。

このadditionalPropertiesが、コード生成時に問題を引き起こすことがある。なぜなら、各プログラミング言語やコードジェネレーターが、この自由形式マップをどのように型付けするかという解釈が大きく異なるためだ。例えば、TypeScriptではRecord<string, V>型、JavaではMap<String, V>型、Goではmap[string]V型のように、値の型が保証されたマップ型が生成されるのが理想的である。しかし、ジェネレーターによっては、単なる汎用的なobject型やdict型にフォールバックしてしまうことがある。この場合、マップの値に関する型情報が完全に失われてしまう。

型情報が失われることの弊害は大きい。開発者はコードを書く際にオートコンプリート(入力補完)の恩恵を受けられず、マップから値を取り出す際に誤った型を仮定してしまう可能性がある。これにより、コンパイル時にはエラーが出なくても、プログラムの実行時に予期せぬエラーやバグが発生するリスクが高まる。しかも、この問題はビルド自体は成功するため、開発の初期段階では気づきにくく、統合テストの段階や、場合によっては本番環境で初めて顕在化することもある。

この問題を解決し、型安全なクライアントコードを生成するための最も重要な対策は、OpenAPIスキーマにおいて、additionalPropertiesで許容するマップの「値」がどのような型を持つのかを明示的に定義することである。単にadditionalProperties: trueとするのではなく、additionalProperties: type: stringのように、値の型を具体的に指定する必要がある。もし値がさらに複雑なオブジェクトである場合でも、そのオブジェクトのプロパティを詳細に定義することで、ジェネレーターに正確な型情報が伝わる。これにより、APIが提供するデータの「契約」が明確になり、ジェネレーターは適切な型定義を生成しやすくなる。

しかし、スキーマを正しく定義したからといって、全てのジェネレーターが期待通りに型を生成するとは限らない。そのため、コードを生成した後には、生成されたクライアントコードのファイルを直接確認し、意図した型が実際に反映されているかを検証することが不可欠である。例えば、TypeScriptのクライアントコードであれば、metadata: Record<string, string>;のように型安全なマップが生成されていることを確認し、もしmetadata: object;のように汎用的な型になっていたら、ジェネレーターの設定を見直すか、型付きマップの生成をサポートする別のジェネレーターの利用を検討する必要がある。また、コードジェネレーターのバージョンを固定し、継続的インテグレーション(CI)環境で生成コードの型チェックを自動化することで、将来的なジェネレーターのアップデートによって予期せず型安全性が損なわれる事態を防ぐことができる。

この設計パターンは「開かれたキー、閉じた値」という考え方に集約される。これは、APIのキー(プロパティ名)は将来的な拡張のために柔軟に(オープンに)しておき、一方でマップの「値」の型は常に厳密に(クローズドに)定義するというアプローチだ。この原則に従うことで、APIの進化に対応しつつ、クライアント側では堅牢な型安全性を維持することが可能となる。もしターゲットとする言語で型付きマップを表現できないジェネレーターであるならば、それはそのジェネレーターの限界として認識し、意図的に対応策を講じるべきであり、開発の遅い段階で偶然発見するような事態は避けるべきだ。

よくある疑問として、キーの集合が固定されていて安定している場合はenumを使用し、より厳密な型付けを行うのが適切である。しかし、キーが任意でユーザーによって追加される可能性がある場合はadditionalPropertiesを使用し、その際には必ず値の型を定義することが重要だ。また、additionalProperties: falseという設定は、スキーマで明示的に定義されていないプロパティを一切許可しないという厳格な意味を持ち、これは厳密なリクエストボディの定義には役立つが、自由形式マップには適さないため、混同しないように注意が必要である。さらに、マップの値の型スキーマも、他のスキーマと同様にnullableやrequiredといった制約を定義できるため、それらが生成されたコードで正しく反映されているかを検証することも重要だ。

このような点に留意することで、OpenAPIとコード生成を組み合わせた開発において、APIの柔軟性を保ちつつ、クライアントコードの型安全性を最大限に高め、開発プロセス全体の品質と効率を向上させることができる。

関連コンテンツ

関連IT用語