Skip to content

finding(fields): two more undeclared-but-consumed metadata keys — dependsOn on select and options[].description on lookup, both reached past the declared type #6153

Description

@yinlianghui-tw

Measured while converting the remaining content/docs/fields pages for #6138 (PR 2). Filed unassigned, not fixed there — the conversion takes both keys out of the annotated blocks (they would redden them); whether they come back as declared members is this card's question.

Same class as #6140 (RichTextField's rows). Filed as one card rather than one per page, because the two instances are the same defect in the same layer and triage should grade them together.

Instance 1 — dependsOn, read off select field metadata through an as any

packages/fields/src/widgets/SelectField.tsx:98:

constdependsOn=(configasany)?.dependsOn??dependsOnProp;

config is the field metadata. SelectFieldMetadata (packages/types/src/field-types.ts) declares options, multiple, searchable and nothing else of its own; BaseFieldMetadata declares depends_on?: string[] — the snake_case sibling — and no dependsOn. The camelCase spelling is declared only as a widget prop (FieldWidgetComponentProps.dependsOn?: DependsOnInput, widgets/types.ts:191), which is the ?? dependsOnProp half of that same line.

So an author who writes dependsOn on a select field's metadata gets working cascading options in the app and a key no exported metadata type accepts — declared and enforced disagree in the direction that hides. content/docs/fields/select.mdx documented it as a metadata key, which is how it was found.

The sibling widget reads both spellings explicitly: LookupField.tsx:292 is fieldMeta?.depends_on ?? fieldMeta?.dependsOn, so the same concept is reachable under two names there, one declared and one not.

Instance 2 — description on a lookup's static options

LookupFieldMetadata.options is declared as SelectOptionMetadata[]. SelectOptionMetadata declares label, value, color, icon, disabled, visibleWhenno description.

LookupField.tsx:250 takes those authored options as the widget's own local type:

conststaticOptions: LookupOption[]=fieldMeta?.options||[];

and LookupField.tsx:526 reads the key:

(opt.description&&opt.description.toLowerCase().includes(q))

LookupOption (LookupField.tsx:38) does declare description?: string — but it is a widget-local interface with an open [key: string]: any, and it is not part of @object-ui/fields's published surface (no occurrence in packages/fields/dist/index.d.ts). The assignment above crosses from the declared metadata type to the open local one without a diagnostic because fieldMeta is untyped.

So a lookup's authored option description is real behaviour (it is searched, and recordToOption at :153 produces the same key for fetched options) that the declared option type rejects.

Candidate resolutions — deliberately not decided here

Both instances turn on the same question, and it is the same question #6140 turns on:

  1. Declare them — add dependsOn to SelectFieldMetadata (or migrate the widget to the declared depends_on), and description to SelectOptionMetadata. Whether the key belongs in @objectstack/spec's authorable surface is not this repo's call alone.
  2. Remove the reads — treat both as accidental surfaces, and let depends_on / a description_field cover the need.

Either way the docs follow the code: content/docs/fields/select.mdx and lookup.mdx (converted in #6138 PR 2) name this card instead of documenting the keys, and will document them as metadata only once a type declares them.

Refs: #6140 (same class, rows) · #6138 (the conversion that measured it) · #4631 / #6121 (declared surfaces that disagree with each other).


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepm:queue

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions