如何在Drizzle ORM查询中使用类型安全的字面量值?
类型安全地在Drizzle ORM查询中使用字面量值
问题场景
在Drizzle ORM中插入字面量值时,必须用sql模板字面量包裹,但这样会丢失自动类型校验;直接使用字面量又不被查询语法支持。自行实现的辅助函数也无法强制字面量匹配目标列的类型,比如传入字符串给数字类型列不会触发TypeScript错误。
原写法(丢失类型安全):
const myScore = 42; const result = await db.insert(game).select( db .select({ player: users.id, score: sql<number>`${myScore}`.as("score"), // 手动指定类型,无自动校验 }) .from(users) .where(eq(users.id, currentUserId)) );
期望的写法:
const result = await db.insert(game).select( db .select({ player: users.id, score: myScore, // 直接使用字面量且保持类型安全 }) .from(users) .where(eq(users.id, currentUserId)) );
自行实现的辅助函数(无类型约束):
function literal<T>(value: T, alias: string) { return sql`${value}`.as(alias); } // 传入非预期类型不会报错 const result = await db.insert(game).select( db .select({ player: users.id, score: literal("not a number", "score"), // 无类型错误,不符合预期 }) .from(users) .where(eq(users.id, currentUserId)) );
解决方案
方案1:基于列类型约束的自定义辅助函数
通过提取Drizzle列的类型信息,让辅助函数强制字面量匹配目标列的类型:
import { sql, Column } from 'drizzle-orm'; // 提取列的数据类型 type ColumnDataType<T extends Column> = T['_']['data']; /** * 类型安全的字面量包装函数 * @param value 要插入的字面量值,类型必须匹配目标列 * @param targetColumn 目标列,用于推导类型约束 * @param alias 查询结果的别名 */ function literal<T extends Column>( value: ColumnDataType<T>, targetColumn: T, alias: string ) { return sql`${value}`.as(alias); } // 使用示例 const result = await db.insert(game).select( db .select({ player: users.id, score: literal(myScore, game.score, "score"), // 传入非number类型会触发TypeScript错误 }) .from(users) .where(eq(users.id, currentUserId)) );
方案2:使用Drizzle官方cast函数(推荐)
Drizzle内置的cast函数可以直接将字面量转换为对应列的类型,自带类型校验:
import { cast } from 'drizzle-orm'; const myScore = 42; const result = await db.insert(game).select( db .select({ player: users.id, score: cast(myScore, game.score).as("score"), // 类型不匹配时TypeScript直接报错 }) .from(users) .where(eq(users.id, currentUserId)) );
原理说明
原辅助函数的泛型T是从传入的value推导而来,而非目标列的类型,因此无法约束值的类型。上述方案通过将目标列的类型作为约束来源,让TypeScript强制校验字面量与列类型的一致性,实现类型安全。
内容的提问来源于stack exchange,提问作者rahokey531
相关产品推荐
相关产品推荐

