payload icon indicating copy to clipboard operation
payload copied to clipboard

Block IDs are always nullable

Open NikoFarrelly opened this issue 8 months ago • 1 comments

Describe the Bug

Kia ora!

My team is currently using payload 2 as a CMS to support our content editing team across our websites.

However when using payload 2, or 3, block IDs are always nullable in the generated payload-types.ts. This is unexpected, as if a block does exist it should have a unique ID attached to it. This is resulting in our team having to add unnecessary conditional checks that the ID does exist for a block.

I've tried adding things such as required: true to improve the type of the generated interface, and it does drop the possibility of it being undefined. However the ID then resolves to being string | null, along with surfacing the ID to CMS editors. Adding hidden: true does solve the latter problem.

Ideally ID types become non-nullable, and will always exist for a given block. Is there already advice on how to achieve this, or am I right in thinking this is a bug, and unexpected behaviour?

Link to the code that reproduces this issue

https://github.com/NikoFarrelly/payload-3-id-nullable

Reproduction Steps

  1. pnpx create-payload-app@latest
    • select 'website' template
    • select 'MongoDB' database
  2. Add a component + config
    • in the repro I've added a 'Panel' component + config
  3. Regenerate types
    • pnpm generate:types
  4. Open payload-types.ts
    • Find 'Panel' interface
  5. Interface will look like this
export interface Panel {
  richText: {
    root: {
      type: string;
      children: {
        type: string;
        version: number;
        [k: string]: unknown;
      }[];
      direction: ('ltr' | 'rtl') | null;
      format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
      indent: number;
      version: number;
    };
    [k: string]: unknown;
  };
  id?: string | null;
  blockName?: string | null;
  blockType: 'panel';
}

Which area(s) are affected? (Select all that apply)

Not sure

Environment Info

Binaries:
  Node: 22.14.0
  npm: 10.9.2
  Yarn: N/A
  pnpm: 9.15.9
Relevant Packages:
  payload: 3.31.0
  next: 15.2.3
  @payloadcms/db-mongodb: 3.31.0
  @payloadcms/email-nodemailer: 3.31.0
  @payloadcms/graphql: 3.31.0
  @payloadcms/live-preview: 3.31.0
  @payloadcms/live-preview-react: 3.31.0
  @payloadcms/next/utilities: 3.31.0
  @payloadcms/payload-cloud: 3.31.0
  @payloadcms/plugin-form-builder: 3.31.0
  @payloadcms/plugin-nested-docs: 3.31.0
  @payloadcms/plugin-redirects: 3.31.0
  @payloadcms/plugin-search: 3.31.0
  @payloadcms/plugin-seo: 3.31.0
  @payloadcms/richtext-lexical: 3.31.0
  @payloadcms/translations: 3.31.0
  @payloadcms/ui/shared: 3.31.0
  react: 19.0.0
  react-dom: 19.0.0
Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 23.5.0: Wed May  1 20:16:51 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T8103
  Available memory (MB): 16384
  Available CPU cores: 8

NikoFarrelly avatar Apr 04 '25 14:04 NikoFarrelly

Having a similar issue with the nested id's

export const Tests: CollectionConfig = {
  slug: 'tests',
  admin: {
    useAsTitle: 'title',
  },
  access: access(),
  fields: [
    {
      name: 'id',
      type: 'text',
      required: true,
    },
    {
      name: 'title',
      label: 'Title',
      type: 'text',
      required: true,
    },
    {
      name: 'questions',
      label: 'Questions',
      type: 'array',
      required: true,
      fields: [
        {
          name: 'id',
          type: 'text',
          required: true,
          defaultValue: crypto.randomUUID(),
        },
        {
          name: 'title',
          label: 'Question Title',
          type: 'text',
          required: true,
        },
        {
          name: 'answers',
          label: 'Answers',
          type: 'array',
          required: true,
          fields: [
            {
              name: 'id',
              type: 'text',
              required: true,
              defaultValue: crypto.randomUUID(),
            },
            {
              name: 'title',
              label: 'Answer Title',
              type: 'text',
              required: true,
            },
            {
              name: 'isCorrect',
              label: 'Is Correct',
              type: 'checkbox',
              defaultValue: false,
            },
            {
              name: 'test-id',
              label: 'test-id',
              type: 'text',
              required: true,
            },
          ],
        },
      ],
    },
  ],
};

In the generated types, the id in the main object has the correct types, but if I have id as an array of fields, in my case test.questions.id or test.questions.answers.id, it has nullable types even if we put the required: true and default values for these fields.

and the same configuration for test-id field generates correct types

Image

It looks like it's a bug only with the fields with a name id somehow.

kholiavko-roman avatar Apr 24 '25 13:04 kholiavko-roman