diff --git a/src/query-builder/select-query-builder.ts b/src/query-builder/select-query-builder.ts
index 46c357dfe..ac93b30fc 100644
--- a/src/query-builder/select-query-builder.ts
+++ b/src/query-builder/select-query-builder.ts
@@ -1558,6 +1558,20 @@ export interface SelectQueryBuilder<DB, TB extends keyof DB, O>
    *
    * functionThatExpectsPersonWithNonNullValue(person)
    * ```
+   *
+   * Giving the explicit narrowed type (`string` in the example above) works fine for
+   * simple types. If the type is complex, for example a JSON column or a subquery,
+   * you can use the special `NotNull` type to make the column not null.
+   *
+   * ```ts
+   * const person = await db.selectFrom('person')
+   *   .where('nullable_column', 'is not', null)
+   *   .selectAll()
+   *   .$narrowType<{ nullable_column: NotNull }>()
+   *   .executeTakeFirstOrThrow()
+   *
+   * functionThatExpectsPersonWithNonNullValue(person)
+   * ```
    */
   $narrowType<T>(): SelectQueryBuilder<DB, TB, NarrowPartial<O, T>>
 
diff --git a/src/util/type-utils.ts b/src/util/type-utils.ts
index dbd9f5ec2..b9817a481 100644
--- a/src/util/type-utils.ts
+++ b/src/util/type-utils.ts
@@ -178,18 +178,35 @@ export type Equals<T, U> = (<G>() => G extends T ? 1 : 2) extends <
   ? true
   : false
 
-export type NarrowPartial<S, T> = DrainOuterGeneric<
+export type NarrowPartial<O, T> = DrainOuterGeneric<
   T extends object
     ? {
-        [K in keyof S & string]: K extends keyof T
-          ? T[K] extends S[K]
+        [K in keyof O & string]: K extends keyof T
+          ? T[K] extends NotNull
+            ? Exclude<O[K], null>
+            : T[K] extends O[K]
             ? T[K]
             : KyselyTypeError<`$narrowType() call failed: passed type does not exist in '${K}'s type union`>
-          : S[K]
+          : O[K]
       }
     : never
 >
 
+/**
+ * A type constant for marking a column as not null. Can be used with `$narrowPartial`.
+ *
+ * Example:
+ *
+ * ```ts
+ * const person = await db.selectFrom('person')
+ *   .where('nullable_column', 'is not', null)
+ *   .selectAll()
+ *   .$narrowType<{ nullable_column: NotNull }>()
+ *   .executeTakeFirstOrThrow()
+ * ```
+ */
+export type NotNull = { readonly __excludeNull__: true }
+
 export type SqlBool = boolean | 0 | 1
 
 /**
diff --git a/test/typings/test-d/select.test-d.ts b/test/typings/test-d/select.test-d.ts
index 9216a62c4..3aa4e8feb 100644
--- a/test/typings/test-d/select.test-d.ts
+++ b/test/typings/test-d/select.test-d.ts
@@ -1,4 +1,12 @@
-import { Expression, Kysely, RawBuilder, Selectable, Simplify, sql } from '..'
+import {
+  Expression,
+  Kysely,
+  NotNull,
+  RawBuilder,
+  Selectable,
+  Simplify,
+  sql,
+} from '..'
 import { Database, Person } from '../shared'
 import { expectType, expectError } from 'tsd'
 
@@ -106,7 +114,24 @@ async function testSelectSingle(db: Kysely<Database>) {
     .$narrowType<NarrowTarget>()
     .execute()
 
-  expectType<NarrowTarget>(r15)
+  // Narrow not null
+  const [r16] = await db
+    .selectFrom('action')
+    .select(['callback_url', 'queue_id'])
+    .$narrowType<{ callback_url: NotNull }>()
+    .execute()
+
+  expectType<string>(r16.callback_url)
+  expectType<string | null>(r16.queue_id)
+
+  const [r17] = await db
+    .selectFrom('action')
+    .select(['callback_url', 'queue_id'])
+    .$narrowType<{ callback_url: NotNull; queue_id: NotNull }>()
+    .execute()
+
+  expectType<string>(r17.callback_url)
+  expectType<string>(r17.queue_id)
 }
 
 async function testSelectAll(db: Kysely<Database>) {