ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Drizzle ORM 0.27.0 版本详解:`is()` 类型守卫、DISTINCT 子句与 SQLite bigint/boolean 支持

Drizzle ORM 0.27.0 版本详解:`is()` 类型守卫、DISTINCT 子句与 SQLite bigint/boolean 支持 Drizzle ORM 0.27.0 版本详解is()类型守卫、DISTINCT 子句与 SQLite bigint/boolean 支持【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm本篇文章围绕 Drizzle ORM 0.27.0 的发布变更记录changelogs/drizzle-orm/0.27.0.md展开逐一拆解该版本的核心能力用自定义is()函数替代instanceof以兼容 monorepo 多实例场景、为查询构建器新增selectDistinct/selectDistinctOnPostgreSQL 专属DISTINCT ON、以及 SQLite 的bigint与boolean模式支持。读完本文你将掌握这些 API 的正确用法、其底层实现原理以及如何在构建 Drizzle 之上层封装时规避多实例带来的类型判断陷阱。一、monorepo 多实例兼容用is()取代instanceof1. 问题背景为什么instanceof会失效在 monorepo 或使用 pnpm/yarn 经典提升hoisting的项目中同一个依赖包可能以多个物理副本存在例如pnpm-lock.yaml中不同版本共存、或node_modules下嵌套安装。此时来自两个不同 Drizzle 实例的Column、Table等类虽然代码同源却是两份不同的类定义。JavaScript 的instanceof基于原型链上的构造函数引用判断跨副本时必然返回false导致基于instanceof的类型检查全面失灵。更严重的连锁反应是maximum call stack exceeded超出最大调用栈当 Drizzle 内部依赖instanceof进行对象识别时多实例环境中会出现无法识别的对象进而触发错误的递归分支最终栈溢出。0.27.0 的发布说明明确提到这修复了当时 Discord 社区中被提及最多的报错之一。2.is()的设计与用法0.27.0 将代码库内部所有的instanceof判断替换为自定义的is()函数。该函数面向所有 Drizzle 实体类Column、Table、View、SQL等提供统一的实例判定能力并且是跨副本安全的。官方推荐用法如下import { is, Column } from drizzle-orm if (is(value, Column)) { // 此处 value 的类型已被收窄为 Column }除了运行时正确性is()还是一个 TypeScript 类型守卫type guard签名会返回value is InstanceTypeT因此在if分支内可获得完整的类型收窄type narrowing效果。3. 源码层面的实现原理is()的实现在 drizzle-orm/src/entity.tsexport const entityKind Symbol.for(drizzle:entityKind); export const hasOwnEntityKind Symbol.for(drizzle:hasOwnEntityKind); export function isT extends DrizzleEntityClassany(value: any, type: T): value is InstanceTypeT { if (!value || typeof value ! object) { return false; } if (value instanceof type) { return true; } if (!Object.prototype.hasOwnProperty.call(type, entityKind)) { throw new Error( Class ${type.name ?? unknown} doesnt look like a Drizzle entity. ..., ); } let cls Object.getPrototypeOf(value).constructor; if (cls) { // Traverse the prototype chain to find the entityKind while (cls) { if (entityKind in cls cls[entityKind] type[entityKind]) { return true; } cls Object.getPrototypeOf(cls); } } return false; }其判断逻辑分三步快速失败非对象值直接返回false兼容传统路径若value instanceof type成立同一副本内直接返回true保持原有性能跨副本兜底遍历value的原型链比较每个构造函数上的entityKind符号标记是否一致。由于entityKind使用Symbol.for(drizzle:entityKind)注册到全局符号表不同物理副本间共享同一个全局 Symbol因此跨副本也能正确匹配。值得注意的是整个代码库中instanceof在is()内仍被保留为快速路径entity.ts同时各类实体通过静态[entityKind]属性声明身份例如PgSelectBuilder中static readonly [entityKind]: string PgSelectBuilder见 drizzle-orm/src/pg-core/query-builders/select.ts。4. 适用场景在 Drizzle API 之上构建自定义工具库、插件或 DSL 时需要判断某个值是否为 Drizzle 实体项目使用 pnpm workspace、lerna、turborepo 等 monorepo 结构依赖被重复安装需要类型收窄的场景is()比手写value instanceof Column更稳健。二、distinct子句支持去重查询一步到位1.selectDistinct通用的 SELECT DISTINCT0.27.0 为查询构建器新增了selectDistinct()方法生成 SQLSELECT DISTINCT ...。发布说明中的示例await db.selectDistinct().from(usersDistinctTable).orderBy( usersDistinctTable.id, usersDistinctTable.name, );从源码看selectDistinct与普通select的唯一差别在于构建器配置中传入distinct: true见 drizzle-orm/src/pg-core/query-builders/query-builder.ts。distinct: true会被PgSelectBuilder保存并在构建 SQL 时转译为DISTINCT关键字该字段的类型定义为boolean | { on: (PgColumn | SQLWrapper)[] }见 drizzle-orm/src/pg-core/query-builders/select.ts。2.selectDistinctOnPostgreSQL 专属的 DISTINCT ONPostgreSQL 独有的DISTINCT ON (expr)语法允许仅对指定列去重同时返回每组的第一行。0.27.0 为其提供了一等公民支持await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( usersDistinctTable.id, ); await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( usersDistinctTable, ).orderBy(usersDistinctTable.name);两种调用形态只传on数组不显式指定选中字段返回全部字段传on数组 字段映射只投影指定的字段集合。在源码中selectDistinctOn会将配置编码为distinct: { on }对象drizzle-orm/src/pg-core/query-builders/query-builder.tson接受PgColumn | SQLWrapper数组意味着既可以直接传列也可以传自定义 SQL 表达式。该能力同样暴露在数据库实例db与独立查询构建器qb两个入口上并支持gel-core的同名实现见 drizzle-orm/src/gel-core/db.ts。3. 使用要点DISTINCT ON只适用于 PostgreSQL含 Neon、Supabase、Vercel Postgres 等基于 PG 的服务MySQL 与 SQLite 请使用selectDistinct()PostgreSQL 要求ORDER BY的起始表达式与DISTINCT ON的表达式保持一致示例中orderBy(usersDistinctTable.id)正是为此否则数据库会直接报错或产生不确定的“每组第一行”若同时需要指定去重列与输出列务必使用双参数形式。三、SQLite 新增bigint与boolean模式SQLite 本身没有原生的bigint与boolean类型0.27.0 通过列模式mode映射补齐了这一能力由社区贡献者 MrRahulRamkumar、raducristianpopa、meech-ward 共同完成。1.bigint模式以 BLOB 存储 64 位整数const users sqliteTable(users, { bigintCol: blob(bigint, { mode: bigint }).notNull(), });在 drizzle-orm/src/sqlite-core/columns/blob.ts 中blob列的类型定义了三档模式buffer | json | bigint见BlobMode类型blob.ts。当指定mode: bigint时底层仍以 BLOB 存储dataType 为bigint读取时mapFromDriverValue将Buffer | Uint8Array | ArrayBuffer转为bigint写入时mapToDriverValue将bigint编码回Bufferblob.ts。由此应用层可以安全使用 JavaScript 的bigint类型处理超过Number.MAX_SAFE_INTEGER约 9 千万亿的整数例如社交平台的用户 ID、支付金额分单位等场景。注意该列不会映射为 SQLite 的INTEGER亲和类型而是 BLOB因此排序、索引行为与整数列不同。2.boolean模式以 INTEGER 0/1 表达布尔值const users sqliteTable(users, { boolCol: integer(bool, { mode: boolean }).notNull(), });SQLite 用INTEGER的 0/1 表达布尔语义。在 drizzle-orm/src/sqlite-core/columns/integer.ts 中SQLiteBoolean继承自SQLiteBaseIntegermode固定为booleanmapFromDriverValue(value: number): boolean将 0/1 转为true/falsemapToDriverValue(value: boolean): number将布尔值写回 0/1integer.ts。同时integer()工厂函数的mode类型扩展为number | timestamp | timestamp_ms | booleaninteger.ts并在构造时根据 mode 分派到对应的 Builder 实现。这样查询结果直接就是 TypeScript 的boolean无需手动!!row.flag转换。3. 兼容性提示bigint模式适合需要精确大整数运算的场景若只是普通整数仍应使用默认的integer列boolean模式让 SQLite 的 0/1 列在类型层面变成真正的布尔值与 PostgreSQL 的boolean列、MySQL 的booleanTINYINT(1)在 Drizzle 类型系统里保持一致的开发体验底层驱动需要能正确处理Buffer/Uint8Array与bigint的序列化主流 SQLite 驱动better-sqlite3、libsql、d1 等均可支持。四、DX 改进与文档更新1. 更友好的类型错误提示关系查询的 verbose 类型错误当你在一个未传入 schema 泛型的数据库类型上使用关系查询relational queries时0.27.0 会抛出更详细的类型错误明确提示缺失 schema 泛型帮助开发者快速定位“为什么我的findMany没有关联类型”这类问题。修复 RQB 中无关系表的where回调此前在关系查询构建器Relational Query BuilderRQB中对没有定义关系的表使用where回调存在缺陷此版本修复保证回调形式的过滤条件在这些表上正常工作。2. 文档改进该版本还包含三处文档修缮修正 docs/joins.md 中的 join 文档拼写错误#522by arjunyel在 README.md 中新增 Supabase 使用指南#690by saltcod以及让 SQLite 列类型文档更加清晰#717by shairez。对于使用 Supabase基于 PostgreSQL接入 Drizzle 的开发者README 中的指南可作为上手参考。五、升级建议与影响面升级前评估如果你在业务代码中直接使用了value instanceof Column之类的判断建议迁移到is(value, Column)以获得 monorepo 环境下的稳定性行为变化内部从instanceof全面切换为is()属于实现层面的替换对外 API 保持兼容正常查询代码无需改动新能力selectDistinct/selectDistinctOn与 SQLite 的bigint/boolean模式均为新增能力可直接使用PostgreSQL 用户可立即体验DISTINCT ON的便捷封装验证路径上述实现均可在本仓库中复现验证——is()见 drizzle-orm/src/entity.tsselectDistinctOn见 drizzle-orm/src/pg-core/query-builders/query-builder.tsSQLite 列模式见 drizzle-orm/src/sqlite-core/columns/blob.ts 与 drizzle-orm/src/sqlite-core/columns/integer.ts单元测试与类型测试位于 drizzle-orm/tests 与 drizzle-orm/type-tests 目录。总而言之Drizzle ORM 0.27.0 以“多实例兼容”为基石修复了 monorepo 场景的顽疾同时补齐了去重查询与 SQLite 类型映射两块实用能力是 Drizzle 在工程健壮性与查询表达力上的一次双线升级。【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表