Skip to content

Mismatch between TypeGen-generated types and actual data: TypeGen generates _type field for non-array object fields, but Sanity Content Lake doesn't store it #19

@KenSCompareCredit

Description

@KenSCompareCredit

Describe the bug

TypeGen generates types that include _type for named object types used as non-array fields, but Sanity's Content Lake does not store the _type field for these objects when there's only one possible type. This causes a mismatch between the generated TypeScript types and the actual query results.

To Reproduce

  1. Install the @sanity/table plugin which defines a named object type table

  2. Create a schema that uses table as a direct object field (not in an array):

    // add-table.ts
    import { defineField, defineType } from 'sanity'
    
    export default defineType({
      name: 'addTable',
      title: 'Table',
      type: 'object',
      fields: [
        defineField({
          name: 'title',
          title: 'Title',
          type: 'string',
        }),
        defineField({
          name: 'table',
          title: 'Table',
          type: 'table', // references the named type from @sanity/table
        }),
      ],
    })
  3. Run sanity schema extract and sanity typegen generate

  4. Query the data using GROQ and observe the actual result vs generated types

Expected behavior

The generated type should match the actual stored data. Since Sanity doesn't store _type for non-array object fields with a single possible type, the generated type should not include it.

Actual behavior

TypeGen generates:

export type Table = {
  _type: 'table' // ← Generated, but NOT in actual data
  rows?: Array<
    {
      _key: string
    } & TableRow
  >
}

But the actual GROQ query result is:

{
  "_type": "addTable",
  "table": {
    "rows": [
      {
        "_key": "d5794e3b-e44d-42b8-b0de-cbdb6032b9df",
        "_type": "tableRow",
        "cells": ["Column 1", "Column 2", "Column 3"]
      }
    ]
  }
}

Note that:

  • addTable has _type: "addTable"
  • table does NOT have _type: "table" ✗ (mismatch with generated type)
  • tableRow (in array) has _type: "tableRow"

Analysis

The extracted schema correctly includes _type for the table type:

{
  "name": "table",
  "type": "type",
  "value": {
    "type": "object",
    "attributes": {
      "_type": {
        "type": "objectAttribute",
        "value": {
          "type": "string",
          "value": "table"
        }
      },
      "rows": { ... }
    }
  }
}

However, Sanity's Content Lake optimizes storage by not storing _type for non-array object fields when there's only one possible type (since it can be inferred from the schema). TypeGen should account for this behavior.

Workaround

Use TypeScript utility types to remove _type from the affected types:

import type { Except } from 'type-fest'
import type { Table } from 'types/sanity.types'

type ActualTable = Except<Table, '_type'>

Which versions of Sanity are you using?

`@sanity/client`: ^7.8.2
`@sanity/table`: ^1.1.3
`sanity`: ^4.10.2

Which versions of Node.js / npm are you running?
npm: 10.9.2
Node: v22.16.0

What operating system are you using?

Linux (Ubuntu 22.04.5 LTS) running inside WSL 2 (v2.4.13.0) within Windows (Windows 11 Pro, version 24H2, OS build 26100.7623)

Additional context

Might be related to #20 which also involves TypeGen generating incorrect _type values that don't match actual query results.

The distinction seems to be:

  • Array members_type IS stored (needed for type discrimination when multiple types are possible)
  • Non-array object fields with single type_type is NOT stored (optimization, can be inferred)

TypeGen should understand this storage behavior and generate types accordingly, perhaps by marking _type as optional for non-array object fields, or omitting it entirely when only one type is possible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions