You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:43:18