2022年12月1日

TypeScript 4.9 の `satisfies` があなたのPrismaワークフローにどう役立つか

TypeScriptの新しいsatisfiesオペレーターは、これまで長い型アノテーションや厄介な回避策が必要だった、新しい型安全なパターンを可能にします。この記事では、一般的なPrisma関連のワークフローでそれが役立ついくつかのユースケースを取り上げます。

How TypeScript 4.9 `satisfies` Your Prisma Workflows

目次

背景

TypeScriptの強みの一つは、コンテキストから式の型を推論できることです。例えば、型アノテーションなしで変数を宣言すると、その型は代入された値から推論されます。これは、値の正確な型が複雑で、明示的に型をアノテーションすると大量の重複コードが必要になる場合に特に便利です。

しかし、時には明示的な型アノテーションが役立つこともあります。それらは他の開発者にコードの意図を伝えるのに役立ち、TypeScriptのエラーを実際の発生源にできるだけ近くに保ちます。

サブスクリプションの価格帯を定義し、NumbertoFixedメソッドを使ってそれらを文字列に変換するコードを考えてみましょう

plansに明示的な型アノテーションを使用すると、タイプミスをより早く検出できるだけでなく、users引数の型を推論できます。しかし、別の問題に遭遇する可能性があります

明示的な型アノテーションを使用すると、型が「ワイド化」され、TypeScriptはどのプランがフラット料金で、どのプランがユーザーごとの料金であるかを判別できなくなります。事実上、アプリケーションの型に関する情報が「失われた」ことになります。

私たちが本当に必要としているのは、ある値が広範な/再利用可能な型と互換性があることをアサートしつつ、TypeScriptに、より狭い(より具体的な)型を推論させる方法です。

制約付き同一性関数

TypeScript 4.9より前は、この問題の解決策は「制約付き同一性関数」を使用することでした。これは、引数と型パラメータを受け取り、その2つが互換性があることを保証する、ジェネリックなno-op関数です。

この種の関数の例としては、Prisma.validatorユーティリティがあります。これは、提供されたジェネリック型で定義された既知のフィールドのみを許可するための追加作業も行います。

残念ながら、この解決策は、コンパイル時にTypeScriptを満足させるためだけに、いくらかのランタイムオーバーヘッドを発生させます。もっと良い方法があるはずです!

`satisfies` の紹介

新しいsatisfiesオペレーターは、ランタイムへの影響なしに同じ利点を提供し、余分なプロパティやスペルミスのプロパティを自動的にチェックします。

TypeScript 4.9では、私たちの価格帯の例がどのように見えるか見てみましょう。

これでタイプミスをソースで直接捕捉できますが、型のワイド化によって情報が「失われる」ことはありません。

この記事の残りの部分では、Prismaアプリケーションでsatisfiesを使用する可能性のある実際の状況をいくつか説明します。

`Prisma.validator` なしでPrismaの出力型を推論する

Prisma Clientは、型安全な結果を得るためにジェネリック関数を使用します。クライアントメソッドから返されるデータの静的型は、クエリで要求した形状と一致します。

これは、Prismaメソッドをインライン引数で直接呼び出す場合に非常にうまく機能します。

しかし、いくつかの落とし穴に遭遇するかもしれません。

  • クエリ引数を小さなオブジェクトに分割しようとすると、型情報が「失われ」(ワイド化され)、Prismaが正しい出力型を推論できない可能性があります。
  • 特定のクエリの出力を表す型を取得するのは難しい場合があります。

satisfiesオペレーターが役立ちます。

`findMany` や `create` のようなメソッドの出力型を推論する

Prismaでsatisfiesオペレーターを使用する最も一般的なユースケースの1つは、findUniqueのような特定のクエリメソッドの戻り値を推論することです。これには、モデルの選択されたフィールドとそのリレーションのみが含まれます。

`count` メソッドの出力型を推論する

Prisma Clientのcountメソッドでは、特定のフィールドにnull以外の値を持つ行をカウントするために、selectフィールドを追加できます。このメソッドの戻り値の型は、指定したフィールドによって異なります。

`aggregate` メソッドの出力型を推論する

より柔軟なaggregateメソッドの出力形状も取得できます。これにより、さまざまなモデルフィールドの平均、最小値、最大値、および数を取得できます。

`groupBy` メソッドの出力型を推論する

groupByメソッドを使用すると、モデルインスタンスのグループに対して集計を実行できます。結果には、グループ化に使用されるフィールドと、集計フィールドの結果が含まれます。ここでは、satisfiesを使用して出力型を推論する方法を示します。

ロスレスなスキーマバリデーターを作成する

スキーマ検証ライブラリ(zodsuperstructなど)は、実行時にユーザー入力をサニタイズするための良い選択肢です。これらのライブラリの中には、スキーマの静的型を推論することで、重複する型定義を減らすのに役立つものもあります。しかし、既存のTypeScript型(Prismaによって生成された入力型など)のスキーマバリデーターを作成したい場合もあります。

例えば、Prismaスキーマファイルに次のようなPost型がある場合

Prismaは以下のPostCreateInput型を生成します。

この型に一致するスキーマをzodで作成しようとすると、スキーマオブジェクトに関する情報が「失われます」。

TypeScript 4.9より前の回避策は、schemaForType関数(制約付き同一性関数の一種)を作成することでした。今ではsatisfiesオペレーターを使用すると、スキーマに関する情報を失うことなく、既存の型に対応するスキーマを作成できます。

ここに、人気のある4つのスキーマ検証ライブラリの例をいくつか示します。

再利用可能なクエリフィルターのコレクションを定義する

アプリケーションが成長するにつれて、多くのクエリで同じフィルタリングロジックを使用するようになるかもしれません。再利用でき、より複雑なクエリに構成できる共通のフィルターを定義したいと思うかもしれません。

いくつかのORMには、これを行うための組み込みの方法があります。例えば、Ruby on Railsではモデルスコープを定義したり、Djangoではカスタムクエリセットメソッドを作成したりできます。

Prismaでは、where条件はオブジェクトリテラルであり、ANDOR、およびNOTと組み合わせて構成できます。satisfiesオペレーターは、再利用可能なフィルターのコレクションを定義するための便利な方法を提供します。

推論された戻り値の型を持つ厳密に型付けされた関数

関数がReactコンポーネントやRemixローダー関数のような特殊な関数シグネチャに一致することをアサートしたい場合があります。Remixローダーのような場合、TypeScriptにその関数が返す特定の形状を推論させたいとも思います。

TypeScript 4.9より前は、これら両方を一度に達成することは困難でした。satisfiesオペレーターを使用すると、戻り値の型をワイド化することなく、関数が特殊な関数シグネチャに一致することを保証できるようになりました。

Prismaからデータを返すRemixローダーの例を見てみましょう。

ここでは、satisfiesオペレーターが3つのことを行います。

  • 私たちのloader関数がRemixのLoaderFunctionシグネチャと互換性があることを保証します。
  • LoaderFunctionシグネチャから関数への引数型を推論するため、手動でアノテーションする必要がありません。
  • 関数がPrismaからPostオブジェクト(関連するcommentsを含む)を返すことを推論します。

まとめ

TypeScriptとPrismaを使用すると、アプリケーションで型安全なデータベースアクセスを簡単に実現できます。PrismaのAPIはゼロコストの型安全性を提供するように設計されているため、ほとんどの場合、「オプトイン」したり、型アノテーションでコードを散らかしたり、ジェネリック引数を提供したりすることなく、自動的に強力な型チェックを得ることができます。

satisfiesオペレーターのような新しいTypeScriptの機能が、より高度なケースでも、最小限の型の煩雑さで、より優れた型安全性を実現するのにどのように役立つかを楽しみにしています。PrismaとTypeScript 4.9をどのように使用しているか、ぜひ私たちのTwitterまでお知らせください。

次の投稿をお見逃しなく!

Prismaニュースレターに登録する

© . All rights reserved.