🛠️

OpenAPI Generator の typescript-fetch をカスタマイズして enum に対応してみた

に公開

はじめに

バックエンドで定義した型定義をフロントエンドでも共有したい!これは、多くの開発者が直面する課題の1つです。
特に OpenAPI を利用して仕様書を出力しているプロジェクトでは、 OpenAPI Generator を活用してフロントエンドの言語に合わせた型定義を自動生成できます。

その中でも TypeScript を利用したフロントエンド開発において typescript-fetch はとても便利で、OpenAPI の仕様書から効率よく型定義を生成可能です。
しかし、実際に利用する中で enum の型定義エラーに出会うことがあります。

今回は、この typescript-fetch に焦点を当て、OpenAPI Generator の出力テンプレートをカスタマイズすることで enum の型定義にも対応できる方法を詳しく解説します。

なぜカスタマイズが必要なのか

まず、 opanapi.json で以下のような enum を定義したとします。

"status": {
  "enum": [
    "作業中",
    "完了",
    "保留中"
  ],
  "type": "string",
}

これをデフォルトの typescript-fetch ジェネレータで出力すると、以下のようになります。

export const ClassnameStatus = {
    : '作業中',
    2: '完了',
    3: '保留中'
} as const;
export type ClassnameStatus = typeof ClassnameStatus[keyof typeof ClassnameStatus];

この出力は enum の key が数値になっているのに加え、0 が出力されず型定義として破綻してしまっています。
問題点として、数値キーの使用により誤った型推論や、0 の出力漏れが発生し、期待される Enum の定義として不整合が生じるという問題があります。
対処法としては以下の方法が考えられます。

  • enum の key を x-enum-varnames に定義する
  • テンプレートを修正して Union 型として出力する

今回は後者のテンプレートを修正して Union 型として出力する方法で修正してみます。

カスタマイズの実装方法

1. テンプレートファイルの準備

OpenAPI Generator はテンプレート出力機能を提供しており、これを使用して既存のテンプレートを取得できます。

# typescript-fetch のテンプレートを出力
npx @openapitools/openapi-generator-cli author template -g typescript-fetch -o ./custom-templates

これにより、typescript-fetch ジェネレータのテンプレートファイルが ./custom-templates ディレクトリに出力されます。

2. テンプレートの修正

出力されたテンプレートの中から、enum の定義部分を探します。以下のようなマスタッシュ構文で書かれているはずです。

{{#stringEnums}}
/**
* @export
* @enum {string}
*/
export enum {{classname}}{{enumName}} {
{{#allowableValues}}
    {{#enumVars}}
    {{{name}}} = {{{value}}}{{^-last}},{{/-last}}
    {{/enumVars}}
{{/allowableValues}}
}
{{/stringEnums}}

これを以下のように修正して、const アサーションと Union 型を使用する形に変更します。

export const {{classname}}{{enumName}} = [
{{#allowableValues}}
    {{#enumVars}}
    {{{value}}}{{^-last}},{{/-last}}
    {{/enumVars}}
{{/allowableValues}}
] as const;
export type {{classname}}{{enumName}} = (typeof {{classname}}{{enumName}})[number]

このテンプレートの修正により、生成されるコードは以下のような形になります。

export const ClassnameStatus = [
    "作業中",
    "完了",
    "保留中"
] as const;
export type ClassnameStatus = typeof ClassnameStatus[keyof typeof ClassnameStatus];

この修正により、Union 型を採用することで型安全性が向上し、IDE のコード補完が効くようになるなどのメリットがあります。

3. カスタマイズしたテンプレートの適用

以下のコマンドで型定義を生成します。

npx @openapitools/openapi-generator-cli generate \
-g typescript-fetch -i ./openapi.yaml -o ./generated

実装結果

変更後は以下のように型を利用できます。

// 型チェックが効く
const status: ClassnameStatus = "作業中";  // OK
const invalid: ClassnameStatus = "不正な値";        // コンパイルエラー

// フォーム入力時のバリデーション
const validateStatus = (input: string) => ClassnameStatus.includes(input)

// その他ドロップダウンリストの生成など、実際のシステムで安全な値の管理に活用できます。

まとめ

OpenAPI Generator のテンプレートをカスタマイズすることで、enum の取り扱いを改善する方法を示しました。

enum に限らずテンプレートを適切にカスタマイズすることで、よりプロジェクトに即した型定義を行えます。

皆様のプロジェクトの型安全性や、フロントエンドとバックエンド間の整合性向上に本記事がお役に立てれば幸いです。

Discussion