定义自定义类型的常用方法

示例

查看 customType 定义如何工作的最佳方法是检查如何使用 Drizzle ORM 中的 customType 函数定义现有的数据类型。

info

每个方言都暴露了 customType 函数

import { customType } from 'drizzle-orm/pg-core';
...
import { customType } from 'drizzle-orm/mysql-core';
...
import { customType } from 'drizzle-orm/sqlite-core';
...
import { customType } from 'drizzle-orm/gel-core';
...
import { customType } from 'drizzle-orm/singlestore-core';

整数(Integer)

import { customType } from 'drizzle-orm/pg-core';

const customSerial = customType<{ data: number; }>(
  {
    dataType() {
      return 'integer';
    },
  },
);

文本(Text)

import { customType } from 'drizzle-orm/pg-core';

const customText = customType<{ data: string }>({
  dataType() {
    return 'text';
  },
});

布尔值(Boolean)

import { customType } from 'drizzle-orm/pg-core';

const customBoolean = customType<{ data: boolean }>({
  dataType() {
    return 'boolean';
  },
});

Jsonb

import { customType } from 'drizzle-orm/pg-core';

const customJsonb = <TData>(name: string) =>
  customType<{ data: TData; driverData: string }>({
    dataType() {
      return 'jsonb';
    },
    toDriver(value: TData): string {
      return JSON.stringify(value);
    },
  })(name);

时间戳(Timestamp)

import { customType } from 'drizzle-orm/pg-core';

const customTimestamp = customType<
  {
    data: Date;
    driverData: string;
    config: { withTimezone: boolean; precision?: number };
  }
>({
  dataType(config) {
    const precision = typeof config.precision !== 'undefined'
      ? ` (${config.precision})`
      : '';
    return `timestamp${precision}${
      config.withTimezone ? ' with time zone' : ''
    }`;
  },
  fromDriver(value: string): Date {
    return new Date(value);
  },
});

所有类型的用法与 Drizzle ORM 中定义的函数相同。例如:

const usersTable = pgTable('users', {
  id: customSerial('id').primaryKey(),
  name: customText('name').notNull(),
  verified: customBoolean('verified').notNull().default(false),
  jsonb: customJsonb<string[]>('jsonb'),
  createdAt: customTimestamp('created_at', { withTimezone: true }).notNull()
    .default(sql`now()`),
});

自定义类型定义的 TS 文档

您可以查看 typesparam 定义的 ts-doc。

export interface CustomTypeValues = {
  /**
   * 自定义列所需的类型,将推断适当的类型模型
   *
   * 示例:
   *
   * 如果您希望您的列在选择/插入后为 `string` 类型 - 使用 `data: string`。如 `text`,`varchar`
   *
   * 如果您希望您的列在选择/插入后为 `number` 类型 - 使用 `data: number`。如 `integer`
   */
  data: unknown;

  /**
 	 * 类型助手,表示数据库驱动程序针对特定数据库数据类型返回的数据类型
 	 *
 	 * 仅在驱动程序的输出和输入类型不同的情况下需要
 	 *
 	 * 默认为 {@link driverData}
 	 */
 	driverOutput?: unknown;

  /**
   * 类型助手,表示数据库驱动程序接受特定数据库数据类型的类型
   */
  driverData?: unknown;

  /**
 	 * 类型助手,表示关系查询聚合为 JSON 后字段返回的类型
 	 */
 	jsonData?: unknown;

  /**
   * {@link CustomTypeParams} `dataType` 生成应使用什么配置类型
   */
  config?: Record<string, unknown>;

  /**
   * 如果您的自定义数据类型应默认不为空,您可以使用 `notNull: true`
   *
   * @example
   * const customSerial = customType<{ data: number, notNull: true, default: true }>({
   *    dataType() {
   *      return 'serial';
   *    },
   * });
   */
  notNull?: boolean;

  /**
   * 如果您的自定义数据类型有默认值,您可以使用 `default: true`
   *
   * @example
   * const customSerial = customType<{ data: number, notNull: true, default: true }>({
   *    dataType() {
   *      return 'serial';
   *    },
   * });
   */
  default?: boolean;
};

export interface CustomTypeParams<T extends CustomTypeValues> {
  /**
   * 用于迁移的数据库数据类型字符串表示
   * @example
   * ```
   * `jsonb`, `text`
   * ```
   *
   * 如果数据库数据类型需要附加参数,您可以从 `config` 参数中使用它们
   * @example
   * ```
   * `varchar(256)`, `numeric(2,3)`
   * ```
   *
   * 要使 `config` 为特定类型,请在 {@link CustomTypeValues} 中使用配置泛型
   *
   * @example
   * 使用示例
   * ```
   *   dataType() {
   *     return 'boolean';
   *   },
   * ```
   * 或
   * ```
   *   dataType(config) {
   *     return typeof config.length !== 'undefined' ? `varchar(${config.length})` : `varchar`;
   *   }
   * ```
   */
  dataType: (config: T['config']) => string;

  /**
   * 可选的映射函数,介于用户输入和驱动程序之间
   * @example
   * 例如,当使用 jsonb 时,我们需要将 JS/TS 对象映射为字符串,然后写入数据库
   * ```
   * toDriver(value: TData): string {
   *   return JSON.stringify(value);
   * }
   * ```
   */
  toDriver?: (value: T['data']) => T['driverData'];

  /**
   * 可选的映射函数,负责将数据从数据库映射到 JS/TS 代码
   * @example
   * 例如,当使用时间戳时,我们需要将字符串日期表示映射为 JS 日期
   * ```
   * fromDriver(value: string): Date {
   *  return new Date(value);
   * },
   * ```
   * 
   * 这会将返回的数据从:
 	 * ```
 	 * {
 	 * 	customField: "2025-04-07T03:25:16.635Z";
 	 * }
 	 * ```
 	 * 改变为:
 	 * ```
 	 * {
 	 * 	customField: new Date("2025-04-07T03:25:16.635Z");
 	 * }
 	 * ```
   */
  fromDriver?: (value: T['driverData']) => T['data'];

  /**
 	 * 可选的映射函数,用于将数据库中以 JSON 形式返回的数据转换为期望的格式
 	 *
 	 * 用于[关系查询](https://orm.drizzle.team/docs/rqb-v2)
 	 *
 	 * 默认为 {@link fromDriver} 函数
 	 * @example
 	 * 例如,当通过 [RQB](https://orm.drizzle.team/docs/rqb-v2) 或 [JSON 函数](https://orm.drizzle.team/docs/json-functions) 查询 bigint 列时,结果字段会以字符串形式返回,而不是常规查询的 bigint 类型
 	 * 针对这种情况,我们需要一个单独的函数来处理字段映射:
 	 * ```
 	 * fromJson(value: string): bigint {
 	 * 	return BigInt(value);
 	 * },
 	 * ```
 	 *
 	 * 这会将返回的数据从:
 	 * ```
 	 * {
 	 * 	customField: "5044565289845416380";
 	 * }
 	 * ```
 	 * 改变为:
 	 * ```
 	 * {
 	 * 	customField: 5044565289845416380n;
 	 * }
 	 * ```
 	 */
 	fromJson?: (value: T['jsonData']) => T['data'];

  /**
 	 * 可选的选择器修改函数,用于修改 [JSON 函数](https://orm.drizzle.team/docs/json-functions) 中列的选择
 	 *
 	 * 额外的映射需求可通过 {@link fromJson} 函数处理
 	 *
 	 * 用于[关系查询](https://orm.drizzle.team/docs/rqb-v2)
 	 * @example
 	 * 例如,使用 bigint 时需要将字段转换为文本以保持数据完整性
 	 * ```
 	 * forJsonSelect(identifier: SQL, sql: SQLGenerator, arrayDimensions?: number): SQL {
 	 * 	return sql`${identifier}::text`
 	 * },
 	 * ```
 	 *
 	 * 这会将查询从:
 	 * ```
 	 * SELECT
 	 * 	row_to_json("t".*)
 	 * 	FROM
 	 * 	(
 	 * 		SELECT
 	 * 		"table"."custom_bigint" AS "bigint"
 	 * 		FROM
 	 * 		"table"
 	 * 	) AS "t"
 	 * ```
 	 * 改变为:
 	 * ```
 	 * SELECT
 	 * 	row_to_json("t".*)
 	 * 	FROM
 	 * 	(
 	 * 		SELECT
 	 * 		"table"."custom_bigint"::text AS "bigint"
 	 * 		FROM
 	 * 		"table"
 	 * 	) AS "t"
 	 * ```
 	 *
 	 * 查询对象返回的数据会从:
 	 * ```
 	 * {
 	 * 	bigint: 5044565289845416000; // 由于直接转换为 JSON 格式导致部分数据丢失
 	 * }
 	 * ```
 	 * 改变为:
 	 * ```
 	 * {
 	 * 	bigint: "5044565289845416380"; // 由于转换字段为文本再进行 JSON 化,数据得到保留
 	 * }
 	 * ```
 	 */
 	forJsonSelect?: (identifier: SQL, sql: SQLGenerator, arrayDimensions?: number) => SQL;
}