From 4e4c8ed3d8634768a480123b0474689583d952b6 Mon Sep 17 00:00:00 2001 From: J0su3Code <255941878+J0su3Code@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:26:42 +0000 Subject: [PATCH] Allow not() to compose with and()/or() results `and()` and `or()` return `SQL | undefined`, but `not()` only accepted `SQLWrapper`. As a result `not(and(...))` and `not(or(...))` raised a TS2345 type error ("'SQL | undefined' is not assignable to 'SQLWrapper'") and forced users to cast the inner expression to `SQLWrapper`. Add an overload so `not()` also accepts `SQLWrapper | undefined` and returns `SQL | undefined`, passing `undefined` straight through the same way `and()` and `or()` already ignore undefined conditions. The original overload still returns `SQL` for a definite condition, so existing call sites keep their exact return type and no behavior changes for them. Add type-test coverage for not(and(...)) and not(or(...)) across the pg, mysql, sqlite, singlestore and geldb dialects. Fixes #1818 --- drizzle-orm/src/sql/expressions/conditions.ts | 13 +++++++++++-- drizzle-orm/type-tests/geldb/select.ts | 2 ++ drizzle-orm/type-tests/mysql/select.ts | 2 ++ drizzle-orm/type-tests/pg/select.ts | 2 ++ drizzle-orm/type-tests/singlestore/select.ts | 2 ++ drizzle-orm/type-tests/sqlite/select.ts | 2 ++ 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drizzle-orm/src/sql/expressions/conditions.ts b/drizzle-orm/src/sql/expressions/conditions.ts index 506e082396..379c47f79b 100644 --- a/drizzle-orm/src/sql/expressions/conditions.ts +++ b/drizzle-orm/src/sql/expressions/conditions.ts @@ -166,16 +166,25 @@ export function or( /** * Negate the meaning of an expression using the `not` keyword. * + * A condition that is equal to `undefined` is passed through as `undefined`, + * so `not` composes with `and`/`or`, which may return `undefined`. + * * ## Examples * * ```ts * // Select cars _not_ made by GM or Ford. * db.select().from(cars) * .where(not(inArray(cars.make, ['GM', 'Ford']))) + * + * // Composes with `and`/`or`, which can return `undefined`. + * db.select().from(cars) + * .where(not(and(eq(cars.make, 'GM'), eq(cars.year, 1950)))) * ``` */ -export function not(condition: SQLWrapper): SQL { - return sql`not ${condition}`; +export function not(condition: SQLWrapper): SQL; +export function not(condition: SQLWrapper | undefined): SQL | undefined; +export function not(condition: SQLWrapper | undefined): SQL | undefined { + return condition === undefined ? undefined : sql`not ${condition}`; } /** diff --git a/drizzle-orm/type-tests/geldb/select.ts b/drizzle-orm/type-tests/geldb/select.ts index e18b74c4b5..0f17e41e3f 100644 --- a/drizzle-orm/type-tests/geldb/select.ts +++ b/drizzle-orm/type-tests/geldb/select.ts @@ -440,6 +440,8 @@ const allOperators = await db ne(users.id, 1), or(eq(users.id, 1), ne(users.id, 1)), not(eq(users.id, 1)), + not(and(eq(users.id, 1), ne(users.id, 1))), + not(or(eq(users.id, 1), ne(users.id, 1))), gt(users.id, 1), gte(users.id, 1), lt(users.id, 1), diff --git a/drizzle-orm/type-tests/mysql/select.ts b/drizzle-orm/type-tests/mysql/select.ts index 2dc826af73..62e3f9c2bc 100644 --- a/drizzle-orm/type-tests/mysql/select.ts +++ b/drizzle-orm/type-tests/mysql/select.ts @@ -308,6 +308,8 @@ const allOperators = await db ne(users.id, 1), or(eq(users.id, 1), ne(users.id, 1)), not(eq(users.id, 1)), + not(and(eq(users.id, 1), ne(users.id, 1))), + not(or(eq(users.id, 1), ne(users.id, 1))), gt(users.id, 1), gte(users.id, 1), lt(users.id, 1), diff --git a/drizzle-orm/type-tests/pg/select.ts b/drizzle-orm/type-tests/pg/select.ts index dcf99428a6..e809f61738 100644 --- a/drizzle-orm/type-tests/pg/select.ts +++ b/drizzle-orm/type-tests/pg/select.ts @@ -439,6 +439,8 @@ const allOperators = await db ne(users.id, 1), or(eq(users.id, 1), ne(users.id, 1)), not(eq(users.id, 1)), + not(and(eq(users.id, 1), ne(users.id, 1))), + not(or(eq(users.id, 1), ne(users.id, 1))), gt(users.id, 1), gte(users.id, 1), lt(users.id, 1), diff --git a/drizzle-orm/type-tests/singlestore/select.ts b/drizzle-orm/type-tests/singlestore/select.ts index 6ec81f6c3f..ae32727647 100644 --- a/drizzle-orm/type-tests/singlestore/select.ts +++ b/drizzle-orm/type-tests/singlestore/select.ts @@ -376,6 +376,8 @@ const allOperators = await db ne(users.id, 1), or(eq(users.id, 1), ne(users.id, 1)), not(eq(users.id, 1)), + not(and(eq(users.id, 1), ne(users.id, 1))), + not(or(eq(users.id, 1), ne(users.id, 1))), gt(users.id, 1), gte(users.id, 1), lt(users.id, 1), diff --git a/drizzle-orm/type-tests/sqlite/select.ts b/drizzle-orm/type-tests/sqlite/select.ts index 0140fc19bb..5f54f46a1b 100644 --- a/drizzle-orm/type-tests/sqlite/select.ts +++ b/drizzle-orm/type-tests/sqlite/select.ts @@ -387,6 +387,8 @@ const allOperators = db ne(users.id, 1), or(eq(users.id, 1), ne(users.id, 1)), not(eq(users.id, 1)), + not(and(eq(users.id, 1), ne(users.id, 1))), + not(or(eq(users.id, 1), ne(users.id, 1))), gt(users.id, 1), gte(users.id, 1), lt(users.id, 1),