Slide 1

Slide 1 text

TypeScript と歩む OpenAPI の discriminator 2025/5/29 @ TSKaigi 2025 本編で話せなかったこと、話し足りなかったこと 株式会社カミナシ Tomoki Sato OpenAPI で Discriminated Union を使いたい

Slide 2

Slide 2 text

自己紹介 株式会社カミナシ ソフトウェアエンジニア 2022/12 カミナシにジョイン 以来、カミナシレポートの開発を行う Tomoki Sato

Slide 3

Slide 3 text

株式会社カミナシ https://kaminashi.jp

Slide 4

Slide 4 text

プロダクトラインナップ カミナシは“現場の基盤”となる 3つの領域をまたぐデジタルインフラに Method 作業 Men ⼈ Machine 設備 電⼦帳票 マニュアル‧研修 コミュニケーション 設備カルテ 1つのアカウント運⽤で複数のシステムを利⽤。運⽤の負担軽減とセキュリティ向上

Slide 5

Slide 5 text

今日話すこと OpenAPI の discriminator との適切な付き合い方を考えること ❏ OpenAPI の discriminator は TypeScript の Discriminated Union を表現 できて便利 ❏ だが、相応のつらみも伴うので付き合い方を考えたい ❏ モチベーション・背景: フロントエンドが複雑なノーコードツールであるカ ミナシレポートで使用したくなることがある

Slide 6

Slide 6 text

OpenAPI の discriminator について ❏ Q. OpenAPI の discriminator とは?

Slide 7

Slide 7 text

OpenAPI の discriminator について ❏ A. これ↓

Slide 8

Slide 8 text

OpenAPI の discriminator について より詳しく ❏ OpenAPI について ❏ Discriminated Union について ❏ OpenAPI の discriminator について

Slide 9

Slide 9 text

OpenAPI の discriminator について OpenAPI について ❏ HTTP API の仕様を記述するためのフォーマット。YAML や JSON で記述 ❏ 統一的なドキュメンテーション ❏ API client・スタブのコード自動生成による開発効率・品質の向上

Slide 10

Slide 10 text

OpenAPI の discriminator について Discriminated Union について ❏ 型を区別するための「しるし」( discriminator ) が付いた特別なユニオン型 ❏ 型の構造で判別するよりも、簡潔に型の判別が行える

Slide 11

Slide 11 text

OpenAPI の discriminator について OpenAPI の discriminator について ❏ OpenAPI には概ね TypeScript の Union や Intersection に相当する記法が ある ❏ Intersection(&)は allOf ❏ Union(|)は oneOf ❏ Discriminated Union は oneOf + discriminator(Discriminator Object)

Slide 12

Slide 12 text

OpenAPI の discriminator について ❏ Intersection(&)は allOf

Slide 13

Slide 13 text

OpenAPI の discriminator について ❏ Union(|)は oneOf

Slide 14

Slide 14 text

OpenAPI の discriminator について ❏ Discriminated Union は oneOf + discriminator

Slide 15

Slide 15 text

OpenAPI の discriminator について ❏ Discriminated Union は oneOf + discriminator (openapi-generator の typescript-axios で自動生成した例)

Slide 16

Slide 16 text

OpenAPI の discriminator のつらみ ❏ API client・スタブが Discriminated Union として生成できるので、一見便 利そう ❏ しかし、つらみもある

Slide 17

Slide 17 text

OpenAPI の discriminator のつらみ ❏ 廃止に向けて議論がされている ❏ Union を直接的に表現する構文を持たないプログラミング言語向けの自動生 成が難しい ❏ TypeScript 向けの API client の自動生成も注意

Slide 18

Slide 18 text

OpenAPI の discriminator のつらみ 廃止に向けて議論がされている ❏ v3.x で非推奨化が議論されていたが、議論は v4 に持ち越された(現時点の 最新は v3.1.1) ❏ v4(Moonwalk) で他の手段への置き換えが議論されているが、結論は出て いない模様 ❏ JSON Schema への準拠を目指す上で、OpenAPI 独自の discriminator は廃 止したい・そもそも discriminator の仕様には不明確な部分があり、指摘さ れている問題も多いことなどが理由

Slide 19

Slide 19 text

OpenAPI の discriminator のつらみ 廃止に向けて議論がされている - 参考リンク ❏ Deprecate discriminator? (https://github.com/OAI/OpenAPI-Specification/issues/2143) ❏ Replace or remove discriminator (https://github.com/OAI/sig-moonwalk/discussions/57)

Slide 20

Slide 20 text

OpenAPI の discriminator のつらみ Union を直接的に表現する構文を持たないプログラミング言語向けの自動生成が 難しい ❏ それはそう。例えば Go や Java。 ❏ openapi-generator の go-server で GO バックエンド向けスタブ生成を試み たが、適切な定義にならないことがある(後述) ❏ openapi-generator の java-microprofile なども同様 ❏ 結局 discriminator (+ oneOf)が有効に扱えるかはエコシステム次第

Slide 21

Slide 21 text

OpenAPI の discriminator のつらみ ❏ openapi-generator の go-server でバックエンド向けスタブ生成 (https://github.com/OpenAPITools/openapi-generator) 詳細割愛するが、異なる型の同 名のフィールドが失われている

Slide 22

Slide 22 text

OpenAPI の discriminator のつらみ TypeScript 向けの API client の自動生成も注意 ❏ openapi-generator の typescript-fetch で Discriminated Union の生成は デフォルトではうまくいかなかった ❏ テンプレートファイル(modelOneOf.mustache)を魔改造すると正しく生 成できた。ただし legacyDiscriminatorBehavior = true の時のみ

Slide 23

Slide 23 text

OpenAPI の discriminator のつらみ TypeScript 向けの API client の自動生成も注意 ❏ とはいえ、試したほとんどの generator では Discriminated Union の生成は 正しくできた ❏ 🟢 openapi-generator の typescript-axios ❏ 🟢 openapi-ts(https://github.com/hey-api/openapi-ts) ❏ 🟢 orval (https://orval.dev/)

Slide 24

Slide 24 text

OpenAPI の discriminator との付き合いかた ❏ では、どうやって OpenAPI の discriminator と付き合えばいいのか?

Slide 25

Slide 25 text

OpenAPI の discriminator との付き合いかた ❏ そもそも OpenAPI の discriminator は使うべきか? ❏ OpenAPI の discriminator を生かす選択肢 〜バックエンド TS の可能性〜 ❏ 楽しみな TypeSpec ❏ カミナシレポートの例

Slide 26

Slide 26 text

OpenAPI の discriminator との付き合いかた そもそも OpenAPI の discriminator は使うべきか? ❏ Discriminated Union 自体の有効性は明らか ❏ OpenAPI 3.x では使える。4.x でも代替手段は検討されている ❏ TypeSpec(後述)でも Discriminated Union を定義でき、OpenAPI Doc を 生成すると discriminator が使われる。代替手段が検討される力学 ⤴⤴

Slide 27

Slide 27 text

OpenAPI の discriminator との付き合いかた OpenAPI の discriminator を生かす選択肢 〜バックエンド TS の可能性〜 ❏ バックエンド(BFF含む)も TypeScript だと Union・Discriminated Union は扱いやすい印象。構造の実行時バリデーションもスムーズ。 (*私にバックエンド TS の経験があまりないのでサンプルを軽く試してみた所感) ❏ Spec First の例: openapi-backend・Zod OpenAPI (https://github.com/openapistack/openapi-backend) (https://hono.dev/examples/zod-openapi) ❏ Code First の例: @nestjs/swagger ( https://docs.nestjs.com/openapi/introduction )

Slide 28

Slide 28 text

OpenAPI の discriminator との付き合いかた 楽しみな TypeSpec ❏ Microsoft が開発・コミュニティがサポートする API 記述言語 (https://learn.microsoft.com/ja-jp/azure/developer/typespec/overview) ❏ 2025/5 に GA に 🎉 ❏ TypeScript にインスパイアされていて、Discriminated Union の表現ももち ろん可能。

Slide 29

Slide 29 text

OpenAPI の discriminator との付き合いかた TypeSpec の Discriminated Union

Slide 30

Slide 30 text

OpenAPI の discriminator との付き合いかた TypeSpec で OpenAPI 3 向けに Emit すると discriminator に...

Slide 31

Slide 31 text

OpenAPI の discriminator との付き合いかた カミナシレポートの例 ❏ カミナシレポートでは、oneOf・discriminator を使っていない ❏ コアなノーコードツール部分をフロントエンドで扱い易い TypeScript の型 で作り込みたい(=Model。詳細次スライド) ❏ バックエンドが Go であるという都合もある

Slide 32

Slide 32 text

OpenAPI の discriminator との付き合いかた カミナシレポートの例 * Model :フレームワーク・API Schema 非依存の型および対応するデータ。DDD における Domain Model のニュアンスが近い

Slide 33

Slide 33 text

OpenAPI の discriminator との付き合いかた まとめ ❏ OpenAPI の discriminator は少なくとも「使ってはいけないもの」ではな い。うまく付き合いましょう。

Slide 34

Slide 34 text

おわり 本日お話した以下の内容が皆さんのフロントエンド・API 設計の一助になれば幸 いです