obsidian/wiki/payloadcms/typescript.md
2026-05-15 16:51:21 +01:00

6.4 KiB

title aliases tags topic sources created updated
Payload CMS — TypeScript
payload-typescript
payloadcms-typescript
payloadcms
typescript
tech-patterns
payloadcms
raw/typescript__overview.md
https://payloadcms.com/docs/typescript/overview
https://payloadcms.com/docs/typescript/generating-types
https://payloadcms.com/docs/typescript/ts-plugin
2026-05-15 2026-05-15

PayloadCMS — TypeScript

Overview

  • Payload is built entirely in TypeScript — native first-class support, no bolt-on.
  • Scaffold a new project: npx create-payload-app@latest → pick a TypeScript template.
  • The generated payload-types.ts file wires up automatic type inference across the entire Local API.

Key Takeaways

  • Payload is TypeScript-native — all internals, config, hooks, and access control are fully typed.
  • Use npx create-payload-app@latest to bootstrap; all templates are TypeScript by default.
  • Run payload generate:types after every schema change to keep payload-types.ts in sync.
  • Export custom reusable types using interfaceName on group/array/block/tab fields.
  • The @payloadcms/typescript-plugin adds IDE path validation and autocomplete for PayloadComponent strings — it does NOT affect tsc or builds.
  • Exported types cover: Config, Collections, Globals, Fields, Hooks, and FormState.

Key Steps / Concepts

Generating Types

Run whenever collections/globals change:

payload generate:types

Add an npm script pointing at your config explicitly (needed when config is not at root):

{
  "scripts": {
    "generate:types": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types"
  }
}

Config Options

// payload.config.ts
{
  typescript: {
    // Default: path.resolve(__dirname, './payload-types.ts')
    outputFile: path.resolve(__dirname, './generated-types.ts'),

    // Disable auto-declare (for cross-repo type sharing)
    declare: false,

    // Extend the JSON schema → extend generated types
    schema: [
      ({ jsonSchema }) => {
        jsonSchema.definitions.MyType = {
          type: 'object',
          properties: { title: { type: 'string' } },
          required: ['title'],
        }
        return jsonSchema
      },
    ],
  },
}

If you disable declare, add the declaration manually:

import { Config } from './payload-types'
declare module 'payload' {
  export interface GeneratedTypes extends Config {}
}

Custom Field Interfaces (interfaceName)

Hoist reusable field types with interfaceName on array, block, group, and named tab fields:

{
  type: 'group',
  name: 'meta',
  interfaceName: 'SharedMeta',  // generates top-level interface
  fields: [
    { name: 'title', type: 'text' },
    { name: 'description', type: 'text' },
  ],
}

Generated output:

export interface SharedMeta {
  title?: string
  description?: string
}

export interface Post {
  meta?: SharedMeta
}

Warning: interfaceName values are top-level — naming collisions with collection slugs will break generation. Suffix with field type (e.g. MetaGroup).

External Schema References

typescript: {
  schema: [
    ({ jsonSchema }) => {
      jsonSchema.definitions.MyType = {
        $ref: './schemas/my-type.json',  // resolved from process.cwd()
      }
      return jsonSchema
    },
  ],
}

Exported Types — Reference

Category Types
Config Config, SanitizedConfig, ClientConfig
Collections CollectionConfig, CollectionSlug
Globals GlobalConfig
Fields Field, TextField, RelationshipField, etc.
Hooks CollectionBeforeChangeHook, GlobalAfterReadHook, etc.
Form state FormState (renamed from Fields in v3)

TypeScript Language Service Plugin (Experimental)

Plugin @payloadcms/typescript-plugin adds IDE intelligence for PayloadComponent path strings.

Features

  • Path validation — red squiggles for non-existent component paths
  • Export validation — catches missing named exports, with "Did you mean?" suggestions
  • Autocomplete — file/dir completions after /, export completions after #
  • Go-to-definition — Cmd+Click on a path string jumps to source

Setup

pnpm add -D @payloadcms/typescript-plugin
// tsconfig.json
{
  "compilerOptions": {
    "plugins": [
      { "name": "next" },
      { "name": "@payloadcms/typescript-plugin" }
    ]
  }
}

VS Code / Cursor — must use workspace TypeScript, not bundled:

  • Cmd+Shift+P → "TypeScript: Select TypeScript Version" → "Use Workspace Version"
  • Then: "TypeScript: Restart TS Server"

Add to .vscode/settings.json for the whole team:

{
  "js/ts.tsdk.path": "node_modules/typescript/lib",
  "js/ts.tsdk.promptToUseWorkspaceVersion": true
}

Supported Path Formats

Format Example
Absolute (from baseDir) '/components/MyField#MyField'
Relative './components/MyField#MyField'
tsconfig alias '@/components/MyField#MyField'
Package import '@payloadcms/ui/rsc#MyComponent'
Default export '/components/MyField'

Both string form and object form ({ path, exportName }) are validated.

baseDir Override

Plugin auto-detects baseDir by walking up to find payload.config.ts. Override when using non-standard layouts:

{
  "compilerOptions": {
    "plugins": [
      {
        "name": "@payloadcms/typescript-plugin",
        "baseDir": "./src"
      }
    ]
  }
}

Common Issues & Fixes

  • Types not updating — rerun payload generate:types; check PAYLOAD_CONFIG_PATH env var
  • declare errors in consuming repo — set declare: false and add manual declaration
  • Plugin not loading — ensure editor uses workspace TS version, not bundled version
  • Naming collision in generated typesinterfaceName clashes with a collection slug; rename with type suffix

Gotchas

  • The TS plugin only runs in the IDE — it does not affect tsc or build output.
  • Path fields _h_slugPath / _h_titlePath appear in generated types but are undefined at runtime unless computeHierarchyPaths: true is passed.
  • Copying payload-types.ts to a frontend repo that does not have payload installed requires declare: false to avoid TS errors.