name: drizzle-orm description: Drizzle ORM 的模式用于类型branding和自定义类型。在使用Drizzle列定义、branded类型或自定义类型转换时使用。
Drizzle ORM 指南
使用 $type<T>() 用于Branded Strings,而不是customType
当你需要一个带有branded TypeScript类型但无需实际数据转换的列时,使用 $type<T>() 而不是 customType。
规则
如果 toDriver 和 fromDriver 是恒等函数 (x) => x,则使用 $type<T>()。
为什么
即使使用恒等函数,customType 仍然在每个行上调用 mapFromDriverValue:
// drizzle-orm/src/utils.ts - 对每个列的每个行运行
const rawValue = row[columnIndex]!;
const value = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
查询1000行,每行有3个日期列 = 3000个函数调用无所事事。
坏的模式
// 运行时开销对于恒等函数
customType<{ data: DateTimeString; driverParam: DateTimeString }>({
dataType: () => 'text',
toDriver: (value) => value, // 在每次写入时调用
fromDriver: (value) => value, // 在每次读取时调用
});
好的模式
// 零运行时开销 - 纯类型断言
text().$type<DateTimeString>();
$type<T>() 是仅编译时的类型覆盖:
// drizzle-orm/src/column-builder.ts
$type<TType>(): $Type<this, TType> {
return this as $Type<this, TType>;
}
何时使用customType
仅当数据在应用程序和数据库之间真正转换时:
// JSON: 对象 ↔ 字符串 - 实际转换
customType<{ data: UserPrefs; driverParam: string }>({
toDriver: (value) => JSON.stringify(value),
fromDriver: (value) => JSON.parse(value),
});
保持数据在中间表示中
优先保持数据序列化(字符串)通过系统,仅在边缘(UI组件)解析。
原则:如果数据以序列化形式进入并以序列化形式离开,保持它在中间序列化。在真正需要丰富表示的边缘解析。
示例:DateTimeString
而不是在数据库层将 DateTimeString 解析为 Temporal.ZonedDateTime:
// 坏:在每次读取时解析,在API边界重新序列化
customType<{ data: Temporal.ZonedDateTime; driverParam: string }>({
fromDriver: (value) => fromDateTimeString(value),
});
保持它为字符串,直到UI真正需要它:
// 好:字符串保持字符串,仅在日期选择器组件解析
text().$type<DateTimeString>();
// 在UI组件中:
const temporal = fromDateTimeString(row.createdAt);
// 编辑后:
const updated = toDateTimeString(temporal);