GraphQL Code Generator(graphql-codegen) おすすめ設定 for TypeScript (original) (raw)
https://zenn.dev/layerx/articles/028cb518cffd61
全体
生成物をフォーマッタにかける
graphql-codegen には Lifecycle Hooks という仕組みがあり、いくつかの用意された hook ポイントで任意のコマンドを実行できる。
これを使って生成されたコードを Prettier 等のフォーマッタに通しておくのがおすすめ。
hooks:
afterAllFileWrite:
- prettier --write
基本 DO NOT EDIT とはいえ、コードジャンプしてきて生成された型を読みたいケースはよくある。
そういうときのために、人間が見やすいように改行しておいてもらうといい。
typescript plugin, typescript-operation plugin
最も基本のプラグイン。ちなみに typescript
はスキーマ、typescript-operation
は Operation(Query, Mutation, Fragment)に関心がある。
設定できる項目は共通のものが多いのでまとめて記載。
immutableTypes
: 意図しないデータ更新を防ぐ
生成される型のフィールドに readonly
がつき、配列は ReadonlyArray
になる。
「クエリ結果を弄ってしまって意図しない箇所に影響が出る」みたいなのを型のレベルで防ぐことができる。
また、関数の引数では readonly であること == 引数を書き換えないことの宣言 になるので、そういう観点でもなるべく readonly にしておきたい。
たまにライブラリが提供する関数の型定義で readonly
ついてなくて面倒になるケースがあるけど、それはその型定義に改善余地ありなので PR を投げてあげましょう。
https://qiita.com/uhyo/items/0fd033ff1aed9b4b32dd
defaultScalarType
: custom scalar を any
にしない
Custom Scalar はデフォルトでは any
になるが、これは最悪なので別の型にマッピングしよう!という設定。
よくあるケースでは Date
や DateTime
のような custom scalar を定義したんだけど、any
になっていることで JS の Date
と勘違いして getFullYear()
呼んじゃってバグる みたいなの。
一番お手軽なのはこの defaultScalarType
で、これを unknown
にしておくことで、前述したようなバグは型チェックで落とせる。
scalars
, strictScalars
: custom scalar を型で区別できるようにする
defaultScalarType
の代わりに入れる、より高度な設定。
scalars
は各 custom scalar ごとにマッピング先の TypeScript の型を指定できる。
たとえば DateString
のような Branded Type をマッピング先に指定することで、unknown
よりもドキュメント・インタフェースとして情報量を増やすことができる。
https://www.wantedly.com/companies/wantedly/post_articles/387161
(余談だけど、上記の記事では適当なフィールドを定義して Branded Type を作っているが、unique symbol を使うほうがより安全という話があるらしい。)
一方で、scalars
で指定が漏れた custom scalar はデフォルトにフォールバックされてしまう。それを防ぐのが strictScalars
。Branded Type を使うなら全部の custom scalar に指定しない理由はないので、scalars
と strictScalars
はセットでの運用が基本になる。
enumsAsConst
: TypeScript の enum を使わない
デフォルトだと GraphQL Enum に対して TypeScript の Enum が出力されるが、それを as const
を使った union 型に変更するオプション。
TypeScript の Enum はいろいろ悩ましいところが多いので、できるだけ利用を避けていきたい。
https://engineering.linecorp.com/ja/blog/typescript-enum-tree-shaking/
skipTypename
: __typename
を使うときは明示しよう
Operation 中で指定しない限りは__typename
が型に出てこなくなる。__typename
が暗黙的に取得されてるかどうかは Client 実装によって異なるので、「自身のコード中で __typename
を使うときは明示的に指定すべき」というルール強制したい、というのがモチベーション。
avoidOptionals
: undefined
を厳密に扱う
nullable なフィールドが optional になるのを防ぐ。
デフォルトの挙動だとfield?: Maybe<T>
だが、avoidOptional
で field: Maybe<T>
になる。
指定したフィールドは null だったら素直に null 返してくる(省略されない)のであれば、実際の挙動に沿った型にしておきたい、という意図。
なお、GraphQL の引数は「値を入れない」と「null を渡す」を区別できるので、Input では avoidOptionals
は無効化しておくほうがいい場合がある。
https://zenn.dev/izumin/articles/710eefc8172f66
avoidOptionals:
field: true
inputValue: false
object: true
defaultValue: false
(これ書いてて気になった: @skip
と @include
が指定されてるフィールドはどういう挙動になるんだっけ?)
add plugin
任意の文字列を挿入できるプラグイン。
eslint-disable
や DO NOT EDIT
などのコメントを追加
graphql-codegen の出力にはヘッダコメントがつかないので、自分でつける必要がある。
自動生成であることをアピールするための DO NOT EDIT
や、ESLint の対象外であることを宣言する /* eslint-disable */
の追加がよくあるパターン。
plugins:
- add:
content: "// Code generated by graphql-codegen. DO NOTT EDIT."
scalars
オプションで使う Branded Type の定義を差し込む
scalars
のところで言及した Branded Type の指定だが、そもそもその型を生成ファイル内で使えないと話にならない。
なので、 add
plugin を使って Branded Type の定義を挿入してあげる。
plugins:
- add:
content: '// Code generated by graphql-codegen. DO NOT EDIT.'
- add:
content: 'export type DateString = string & { readonly brand: unique symbol };'
- typescript
config:
scalars:
Date: DateString
別のファイルで Branded Type を定義して import してくるのでもいいかもしれない。
typed-document-node plugin
GraphQL Document の TypedDocumentNode を生成する。
詳しくは以下の記事や動画を見てもらえばいいが、簡単に言うと「TypedDocumentNode
を useQuery
に渡すだけで variables やレスポンスに型がつく」もの。
https://the-guild.dev/blog/typed-document-node
https://graphql.wtf/episodes/41-typed-document-node
JS の GraphQL エコシステムはだいたい TypedDocumentNode に対応してるので、特定のライブラリのラッパー関数を生成する plugin を使うよりも多くの場合で typed-document-node
plugin のほうがいいはず。
typedscript-msw plugin
名前の通り、MSW のハンドラを定義するためのヘルパを生成してくれる。
だいたいこんなかんじ(例は plugin ページから引用)。
// mockGetUserQuery が生成される関数
mockGetUserQuery((req, res, ctx) => {
const { id } = req.variables // ← 型がついてる
return res(
ctx.data({
getUser: { name: 'John Doe', id } ← 型がついてる
})
)
})
テストや Storybook のお供にどうぞ。
near-operation-files preset
GraphQL Operation に対応する生成物を、その Operation が記述されてるファイルの近くに書き出してくれる。
- Query や Fragment を使う場所の近くに型定義がある方が見やすい
- ひとつひとつの生成ファイルが小さくなって見やすい
というような生産性観点のメリットもあるし、以下の記事にあるようなパフォーマンス上のメリットもある。事情がない限りは入れておきたい。
https://blog.hiroppy.me/entry/2021/08/12/092839
gql-tag-operations preset
これは超特殊だし発展途上だけど非常に良いものなので紹介… と言いたいところだが、話がかなり逸れるので別記事で紹介します。
次期メジャーである GraphQL Code Generator v3 はこの gql-tag-operations の方向に進化しそうな雰囲気もあるので楽しみですね。
https://github.com/dotansimha/graphql-code-generator/issues/8296
さいごに
みなさんのオススメ設定もあれば是非コメントして下さい!