Laizy CMS
Client

TypeScript Types

Understanding the generated TypeScript interfaces, input types, and utility types from the Laizy client generator.

TypeScript Types

The laizy generate command produces a complete type system from your schema. Every content model gets three interfaces: a read type, a create input type, and an update input type. These types power autocomplete, compile-time validation, and documentation for your content layer.

Generated Interface Categories

For each model in your schema, three interfaces are generated:

Model Interface (Read Type)

The full representation of a content entry, including system fields. This is what you get back from findMany() and findById().

Given this schema:

model BlogPost {
  title: String {
    required: true
    maxLength: 200
  }
  content: String {
    required: true
  }
  status: String {
    default: "draft"
  }
  slug: String {
    required: true
    unique: true
  }
}

The generator produces:

export interface BlogPost {
  title: string
  content: string
  status: string
  slug: string
  // System fields
  id: string
  createdAt: Date
  updatedAt: Date
}

All fields are non-optional in the read type because every stored entry has values for all fields (either explicitly set or filled with defaults).

Create Input Type

The interface for creating new content. Only includes user-defined fields, with optionality based on the required constraint. Fields with default values are excluded since they have a fallback.

export interface CreateBlogPostInput {
  title: string      // required: true, no default
  content: string    // required: true, no default
  slug: string       // required: true, no default
  // 'status' is excluded because it has default: "draft"
}

Rules for Create inputs:

ConstraintResult in CreateInput
required: true, no defaultRequired field (no ?)
required: true, has defaultExcluded from interface
No required (optional), no defaultOptional field (?)
No required, has defaultExcluded from interface

Update Input Type

The interface for partial updates. All fields are optional since you only send the fields you want to change.

export interface UpdateBlogPostInput {
  title?: string
  content?: string
  status?: string
  slug?: string
}

Every user-defined field is optional with ?. System fields (id, createdAt, updatedAt) are never included in update inputs since they are managed by the system.

Type Mapping

Schema field types map to TypeScript types as follows:

Schema TypeTypeScript TypeExample Value
Stringstring"Hello World"
Intnumber42
Floatnumber3.14
Booleanbooleantrue
DateTimeDatenew Date("2024-01-15")

Both Int and Float map to TypeScript's number type. The distinction between integers and floats is enforced at the schema validation level, not at the TypeScript type level.

System Fields

Every content model automatically includes three system fields. You do not define them in your schema, but they appear in the generated read interface:

// System fields added to every model
id: string         // Unique identifier
createdAt: Date    // Creation timestamp
updatedAt: Date    // Last modification timestamp

System fields are:

  • Present in the read type (e.g., BlogPost)
  • Absent from CreateInput and UpdateInput (managed by the system)
  • Returned by all query methods (findMany(), findById())

FindManyOptions

A generic utility type is generated for configuring list queries:

export interface FindManyOptions<T> {
  where?: Partial<T>
  select?: Partial<Record<keyof T, boolean>>
  limit?: number
  offset?: number
}

This type is used by the findMany() method:

// Full type safety on the options
const posts = await client.blogPost.findMany({
  limit: 10,
  offset: 0,
});
OptionTypeDescription
wherePartial<T>Filter criteria (field value matching)
selectPartial<Record<keyof T, boolean>>Field selection
limitnumberMaximum results to return
offsetnumberNumber of results to skip (for pagination)

Multi-Model Example

When your schema has multiple models, each gets its own set of interfaces:

model Author {
  name: String {
    required: true
    maxLength: 100
  }
  email: String {
    required: true
    unique: true
  }
  bio: String
}

model HeroSection {
  badge: String
  headline: String
  subheading: String
  ctaPrimary: String
  ctaSecondary: String
}

Generated types:

// Author types
export interface Author {
  name: string
  email: string
  bio: string
  id: string
  createdAt: Date
  updatedAt: Date
}

export interface CreateAuthorInput {
  name: string       // required
  email: string      // required
  bio?: string       // optional
}

export interface UpdateAuthorInput {
  name?: string
  email?: string
  bio?: string
}

// HeroSection types
export interface HeroSection {
  badge: string
  headline: string
  subheading: string
  ctaPrimary: string
  ctaSecondary: string
  id: string
  createdAt: Date
  updatedAt: Date
}

export interface CreateHeroSectionInput {
  badge: string
  headline: string
  subheading: string
  ctaPrimary: string
  ctaSecondary: string
}

export interface UpdateHeroSectionInput {
  badge?: string
  headline?: string
  subheading?: string
  ctaPrimary?: string
  ctaSecondary?: string
}

Using Types in Your Code

Import types directly from the generated module:

import type { BlogPost, CreateBlogPostInput, Author } from './generated/laizy';

// Type-safe function signatures
async function getPublishedPosts(): Promise<BlogPost[]> {
  return client.blogPost.findMany();
}

// Type-safe content creation
const newPost: CreateBlogPostInput = {
  title: 'My New Post',
  content: 'Post content here',
  slug: 'my-new-post',
};

The types are also available through the barrel export:

import type { BlogPost } from './generated/laizy';
// Equivalent to importing from './generated/laizy/types'

Regeneration

The generated types are fully overwritten each time you run pnpm laizy generate. The files include a // DO NOT EDIT - This file is auto-generated header as a reminder.

If you need to extend the types, create your own interfaces that compose the generated ones:

import type { BlogPost } from './generated/laizy';

// Extend with your own fields
interface BlogPostWithComments extends BlogPost {
  comments: Comment[];
}

Next Steps

On this page