drizzle-team / drizzle-team/drizzle-orm

[FEATURE]: Allow for different typing for select and insert in custom types

Open
#4,348 1 comment 2 reactions 0 assignees View on GitHub
enhancement qb/crud
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

### Feature hasn't been suggested before.

- [x] I have verified this feature I'm about to request hasn't been suggested before.

### Describe the enhancement you want to request

Currently, customType has 1 typing for the desired data type outside of the DB. I would like to suggest an option that would split that into 2 types. Something like this:

```ts
customType<{
data: unknown; // represents select data
mutationData: object; // represents mutations (insert, update) data. This can default to data.
}>({
dataType(config) {
},

// The type for data here should be mutationData
toDriver(data) {
}

// The type for data here should be data
fromDriver(data) {
}
});
```

This is useful for types where valid and invalid data represents 2 different types. For example, the [Luxon](https://moment.github.io/luxon/) date library has a type of `DateTime` by default, where the boolean represents if it's valid. A validated value's type then becomes `DateTime`, which changes the output type of a few methods (eg the `toString()` method changes from `string | null` to `string`).

With the above change, I can then add validation within the customType itself and have it output the proper type.

```ts
const ts = customType<{
data: DateTime;
mutationData: DateTime;
driverData: string;
config: { withTimezone?: boolean; precision?: number };
}>({
dataType(config) {
const { precision: precisionOpt, withTimezone = true } = config ?? {};
const precision =
typeof precisionOpt !== "undefined" ? ` (${precisionOpt})` : "";
return `timestamp${precision}${withTimezone ? " with time zone" : ""}`;
},

// this is automatically typed as DateTime;
toDriver(data) {
if (!assertValidDateTime(data)) {
throw new Error(
"[drizzle][customTimestamp] Invalid DateTime. Reason: " +
(data as DateTime).invalidReason
);
}
return data.setZone("utc").toISO();
},

// this will error out if the type isn't DateTime, i.e. not validated yet;
fromDriver(data) {
const res = DateTime.fromSQL(data);
if (!assertValidDateTime(res)) {
throw new Error(
"[drizzle][customTimestamp] Invalid DateTime. Reason: " +
(res as DateTime).invalidReason
);
}
return res;
},
});

const table = pgTable("test", {
createdAt: ts("created_at")
});

await db.insert(table).values({
createdAt: DateTime.fromISO("2025-01-01T00:00:00Z") // this will not error out, since it is a DateTime type
});

const createdAt = (await db.select().from(table))[0]; // this will automatically be DateTime, since validation is already done.
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.