Skip to main content

Type Signatures

type Generated<S> = ColumnType<S, S | undefined, S>

type GeneratedAlways<S> = ColumnType<S, never, never>

Overview

These utility types are shortcuts for common patterns when working with database-generated columns:
  • Generated<T> - For columns that are optional in inserts and updates (e.g., auto-increment IDs, default values)
  • GeneratedAlways<T> - For columns that cannot be inserted or updated (e.g., GENERATED ALWAYS AS IDENTITY columns)

Generated

The Generated<T> type is useful for columns that the database can generate automatically, but can also be provided manually.

Type Parameters

S
type
required
The TypeScript type for the column across all operations.

Behavior

  • Select: Returns type S
  • Insert: Accepts S | undefined (optional)
  • Update: Accepts S (optional, as all update fields are optional)

Examples

Auto-Increment ID

interface PersonTable {
  id: Generated<number>
  name: string
}

// Insert without ID (database generates it)
await db.insertInto('person')
  .values({ name: 'John' })
  .execute()

// Insert with explicit ID
await db.insertInto('person')
  .values({ id: 123, name: 'Jane' })
  .execute()

// Select returns number
const person = await db
  .selectFrom('person')
  .selectAll()
  .executeTakeFirst()
// person.id is number

Timestamp with Default Value

interface PostTable {
  id: Generated<number>
  title: string
  created_at: Generated<Date>
}

// created_at is optional in insert
await db.insertInto('post')
  .values({ title: 'Hello World' })
  .execute()

GeneratedAlways

The GeneratedAlways<T> type is for columns that are always generated by the database and cannot be manually set.

Type Parameters

S
type
required
The TypeScript type returned when selecting this column.

Behavior

  • Select: Returns type S
  • Insert: Not allowed (never)
  • Update: Not allowed (never)

Examples

PostgreSQL GENERATED ALWAYS AS IDENTITY

CREATE TABLE person (
  id INTEGER GENERATED ALWAYS AS IDENTITY,
  name TEXT NOT NULL
);
interface PersonTable {
  id: GeneratedAlways<number>
  name: string
}

// Insert - cannot provide id
await db.insertInto('person')
  .values({ name: 'John' })
  .execute()

await db.insertInto('person')
  .values({ id: 123, name: 'Jane' }) // Type error!
  .execute()

// Update - cannot update id
await db.updateTable('person')
  .set({ id: 456 }) // Type error!
  .where('name', '=', 'John')
  .execute()

Computed Columns

CREATE TABLE product (
  price DECIMAL(10, 2),
  tax_rate DECIMAL(4, 2),
  total_price DECIMAL(10, 2) GENERATED ALWAYS AS (price * (1 + tax_rate)) STORED
);
interface ProductTable {
  price: number
  tax_rate: number
  total_price: GeneratedAlways<number>
}

// total_price is read-only
await db.insertInto('product')
  .values({
    price: 100,
    tax_rate: 0.2
  })
  .execute()

const product = await db
  .selectFrom('product')
  .selectAll()
  .executeTakeFirst()
// product.total_price is 120

Comparison

TypeSelectInsertUpdateUse Case
Generated<T>TT | undefinedTAuto-increment IDs, default values
GeneratedAlways<T>TneverneverIdentity columns, computed columns

Source

View source on GitHub

Build docs developers (and LLMs) love