2022 年 12 月 01 日

TypeScript 4.9 的 `satisfies` 如何满足您的 Prisma 工作流程

TypeScript 新的 satisfies 操作符允许一些新的、类型安全的模式,这些模式以前需要冗长的类型注解或棘手的方法才能实现。本文介绍了它在常见 Prisma 相关工作流程中为您提供帮助的几个用例。

How TypeScript 4.9 `satisfies` Your Prisma Workflows

目录

一点背景知识

TypeScript 的优势之一是它可以从上下文中推断表达式的类型。例如,您可以声明一个没有类型注解的变量,其类型将从您分配给它的值中推断出来。当值的确切类型复杂,并且显式注解类型需要大量重复代码时,这尤其有用。

但是,有时显式类型注解很有用。它们可以帮助向其他开发人员传达代码的意图,并使 TypeScript 错误尽可能接近错误的实际来源。

考虑一些代码,这些代码定义了订阅定价层级,并使用 toFixed 方法将它们转换为字符串 Number

如果我们在 plans 上使用显式类型注解,我们可以更早地捕获拼写错误,并推断 users 参数的类型。但是,我们可能会遇到不同的问题

当我们使用显式类型注解时,类型会“扩大”,TypeScript 无法再分辨出我们的哪些计划具有固定价格,哪些计划具有按用户定价。实际上,我们“丢失”了一些关于应用程序类型的信息。

我们真正需要的是一种方法,可以断言一个值与某些广泛/可重用的类型兼容,同时让 TypeScript 推断出更窄(更具体)的类型。

约束标识函数

在 TypeScript 4.9 之前,解决此问题的方法是使用 “约束标识函数”。这是一个通用的、无操作的函数,它接受一个参数和一个类型参数,确保两者兼容。

这种函数的示例是 Prisma.validator 实用程序,它还做了一些额外的工作,只允许使用提供的泛型类型中定义的已知字段。

不幸的是,这种解决方案会产生一些运行时开销,仅仅是为了在编译时让 TypeScript 满意。一定有更好的方法!

介绍 satisfies

新的 satisfies 操作符提供了相同的好处,没有运行时影响,并自动检查多余或拼写错误的属性。

让我们看看 TypeScript 4.9 中我们的定价层级示例可能是什么样子

现在我们在源头就捕获了拼写错误,但我们没有“丢失”任何类型扩展的信息。

本文的其余部分将介绍一些您可能在 Prisma 应用程序中使用 satisfies 的实际情况。

无需 Prisma.validator 推断 Prisma 输出类型

Prisma Client 使用泛型函数为您提供类型安全的结果。从客户端方法返回的数据的静态类型与您在查询中要求的形状相匹配。

当直接使用内联参数调用 Prisma 方法时,这非常有效

但是,您可能会遇到一些陷阱

  • 如果您尝试将查询参数分解为更小的对象,类型信息可能会“丢失”(扩大),并且 Prisma 可能无法正确推断输出类型。
  • 很难获得表示特定查询输出的类型。

satisfies 操作符可以提供帮助。

推断诸如 findManycreate 等方法的输出类型

对于 Prisma,satisfies 操作符最常见的用例之一是推断特定查询方法(如 findUnique)的返回类型——包括仅选择的模型字段及其关系。

推断 count 方法的输出类型

Prisma Client 的 count 方法允许您添加一个 select 字段,以便计算指定字段的非空值的行数。此方法的返回类型取决于您指定的字段

推断 aggregate 方法的输出类型

我们还可以获得更灵活的 aggregate 方法的输出形状,该方法使我们能够获得各种模型字段的平均值、最小值、最大值和计数

推断 groupBy 方法的输出类型

groupBy 方法允许您对模型实例组执行聚合。结果将包括用于分组的字段,以及聚合字段的结果。以下是如何使用 satisfies 推断输出类型

创建无损模式验证器

模式验证库(例如 zodsuperstruct)是在运行时清理用户输入的不错选择。其中一些库可以通过推断模式的静态类型来帮助您减少重复的类型定义。但是,有时您可能想要为现有的 TypeScript 类型(例如 Prisma 生成的输入类型)创建模式验证器。

例如,给定 Prisma 模式文件中类似这样的 Post 类型

Prisma 将生成以下 PostCreateInput 类型

如果您尝试使用 zod 创建与此类型匹配的模式,您将“丢失”有关模式对象的一些信息

在 TypeScript 4.9 之前的解决方法是创建一个 schemaForType 函数(一种约束标识函数)。现在使用 satisfies 操作符,您可以为现有类型创建模式,而不会丢失有关模式的任何信息。

以下是四个流行的模式验证库的一些示例

定义可重用的查询过滤器集合

随着应用程序的增长,您可能会在许多查询中使用相同的过滤逻辑。您可能想要定义一些通用过滤器,这些过滤器可以重用并组合成更复杂的查询。

一些 ORM 具有内置的方法来执行此操作——例如,您可以在 Ruby on Rails 中定义 模型作用域,或在 Django 中创建 自定义查询集方法

使用 Prisma,where 条件是对象字面量,可以使用 ANDORNOT 组合。satisfies 操作符为我们提供了一种定义可重用过滤器集合的便捷方法

具有推断返回类型的强类型函数

有时您可能想要断言函数与特殊函数签名匹配,例如 React 组件或 Remix 加载器函数。在 Remix 加载器等情况下,您还希望 TypeScript 推断函数返回的特定形状。

在 TypeScript 4.9 之前,很难同时实现这两者。借助 satisfies 操作符,我们现在可以确保函数与特殊函数签名匹配,而不会扩大其返回类型。

让我们看一下 Remix 加载器的示例,该加载器从 Prisma 返回一些数据

在这里,satisfies 操作符执行三项操作

  • 确保我们的 loader 函数与 Remix 中的 LoaderFunction 签名兼容
  • LoaderFunction 签名中推断我们函数的参数类型,因此我们不必手动注解它们
  • 推断我们的函数返回来自 Prisma 的 Post 对象,包括其相关的 comments

总结

TypeScript 和 Prisma 使在您的应用程序中获得类型安全的数据库访问变得容易。Prisma 的 API 旨在提供零成本类型安全,因此在大多数情况下,您可以自动获得强大的类型检查,而无需“选择加入”、使用类型注解使代码变得混乱或提供泛型参数。

我们很高兴看到像 satisfies 操作符这样的新 TypeScript 功能如何在更高级的情况下,以最小的类型噪音帮助您获得更好的类型安全性。请在我们的 Twitter 上联系我们,让我们知道您是如何使用 Prisma 和 TypeScript 4.9 的。

不要错过下一篇文章!

注册 Prisma Newsletter