TypeScript 新的 `satisfies` 运算符允许使用一些以前需要冗长类型注解或巧妙变通的新型安全模式。本文将介绍它在哪些常见的 Prisma 相关工作流程中能为您提供帮助。
目录
背景介绍
TypeScript 的优势之一在于它能够从上下文中 推断 表达式的类型。例如,您可以声明一个不带类型注解的变量,其类型将从您为其赋的值中推断出来。当值的确切类型很复杂,并且显式注解类型会需要大量重复代码时,这尤其有用。
然而,有时显式类型注解也很有用。它们可以帮助向其他开发者传达代码的 意图,并使 TypeScript 错误尽可能接近实际的错误来源。
考虑一些定义订阅定价层并使用 `Number` 上的 `toFixed` 方法将其转换为字符串的代码
如果我们对 `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` 运算符可以提供帮助。
推断 `findMany` 和 `create` 等方法的输出类型
`satisfies` 运算符与 Prisma 最常见的用例之一是推断特定查询方法(如 `findUnique`)的返回类型——包括模型及其关系中仅选择的字段。
推断 `count` 方法的输出类型
Prisma Client 的 `count` 方法允许您添加一个 `select` 字段,以便计算指定字段非空值的行数。此方法的返回类型取决于您指定的字段
推断 `aggregate` 方法的输出类型
我们还可以获取更灵活的 `aggregate` 方法的输出形状,该方法允许我们获取各种模型字段的平均值、最小值、最大值和计数。
推断 `groupBy` 方法的输出类型
`groupBy` 方法允许您对模型实例组执行聚合。结果将包括用于分组的字段,以及聚合字段的结果。以下是您如何使用 `satisfies` 推断输出类型的方法
创建无损模式验证器
模式验证库(例如 zod 或 superstruct)是运行时清理用户输入的不错选择。其中一些库可以通过推断模式的静态类型来帮助您减少重复的类型定义。但是,有时您可能希望为现有 TypeScript 类型(例如 Prisma 生成的输入类型)创建模式验证器。
例如,在您的 Prisma 模式文件中给出像这样的 `Post` 类型
Prisma 将生成以下 `PostCreateInput` 类型
如果您尝试使用 zod 创建与此类型匹配的模式,您将“丢失”有关模式对象的一些信息
在 TypeScript 4.9 之前,一种解决方法是创建一个 schemaForType
函数(一种受约束的恒等函数)。现在,使用 `satisfies` 运算符,您可以为现有类型创建模式,而不会丢失任何有关模式的信息。
以下是四种流行模式验证库的一些示例
定义可重用的查询过滤器集合
随着应用程序的增长,您可能会在许多查询中使用相同的过滤逻辑。您可能希望定义一些可以重用并组合成更复杂查询的通用过滤器。
一些 ORM 具有内置的方法来做到这一点——例如,您可以在 Ruby on Rails 中定义模型作用域,或者在 Django 中创建自定义查询集方法。
使用 Prisma,`where` 条件是对象字面量,可以与 `AND`、`OR` 和 `NOT` 组合。`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 新闻通讯