Skip to content

List/array scalar columns dropped from accessor field types (no .value/.setValue) #58

Description

@jindrak02

Summary

A native list/array scalar column — a field typed readonly T[] where T is a primitive/enum (string), not an entity — is dropped from every branch of the accessor field-type mapping. The accessor proxy exposes no FieldAccessor for it, so .value / .setValue don't exist on the type and reading/writing the column from createComponent explicit selection fails to compile. The runtime FieldHandle handles array columns fine; only the types are wrong.

Environment

Reproduction

A Contember enumColumn(...).list() (or any .list() scalar) generates an entity field typed as readonly T[] with T a string enum:

import type { ScalarKeys, FieldAccessor, EntityAccessor } from '@contember/bindx-react'

type AssertExtends<T, U> = [T] extends [U] ? true : false
function assertTrue<T extends true>(): void {}

interface Lesson {
	id: string
	title: string
	// native list/array SCALAR column — array of a string enum, not a relation
	groupSize: readonly ('whole' | 'group' | 'individual')[]
}

// FAILS: groupSize is not in ScalarKeys<Lesson> (which is only 'id' | 'title')
assertTrue<AssertExtends<'groupSize', ScalarKeys<Lesson>>>()

type LessonAcc = EntityAccessor<Lesson, { groupSize: readonly string[] }>
type GroupSizeField = LessonAcc['$fields']['groupSize']
// FAILS: the field is not a FieldAccessor, so it has no .value / .setValue
assertTrue<AssertExtends<GroupSizeField, FieldAccessor<readonly string[]>>>()

Both assertTrue lines error with TS2344: Type 'false' does not satisfy the constraint 'true'.

Expected behavior

An array-valued scalar column is still a scalar field — it should be classified as a ScalarKeys member and its accessor should be FieldAccessor<readonly T[]>, with .value returning the array and .setValue(next) replacing it. (FieldRefMeta already carries an isArray flag, so the model anticipates array fields.)

Actual behavior

The field is excluded from all three key sets in packages/bindx/src/handles/types.ts:

  • ScalarKeys<T> (lines ~53–61): T[K] extends (infer _U)[] ? never : … — any array-valued field is excluded outright.
  • HasManyKeys<T> (lines ~63–69): T[K] extends (infer U)[] ? U extends object ? K : never : never — an array of a non-object element is excluded.
  • HasOneKeys<T> (lines ~71–79): excluded because it IS an array.

EntityFieldsAccessor then maps only over (ScalarKeys | HasManyKeys | HasOneKeys) & keyof TSelected, so the column has no entry and resolves through the base proxy intersection to a HasOne-style accessor with no .value / .setValue.

Suspected root cause

ScalarKeys<T> treats "is an array" as "is not a scalar". For columns this is wrong: a .list() scalar column is array-valued but is still a column, not a relation. HasManyKeys only matches arrays of objects (entities), so array-of-primitive columns fall through every branch.

Suggested fix

Make ScalarKeys<T> keep array-valued keys whose element is not an object (i.e. scalar arrays), and ensure FieldAccessorType / EntityFieldsAccessor route them to FieldAccessor<TEntity[K]>. Roughly: in ScalarKeys, an array key should be never only when its element extends object (that's a has-many); a scalar-element array stays a scalar key. The runtime FieldHandle already supports array columns, so this is a type-only change in packages/bindx/src/handles/types.ts. Maintainers may prefer a dedicated ScalarArrayKeys branch — deferring the exact shape to you.

Workaround shipped downstream

We applied a temporary workaround in our project, marked
TODO [BindX] (<this-issue-url>): …. The workaround narrows the list-enum field accessor to a precise local interface exposing value/setValue (no as any) at the two read/write sites in the material corrector form; we will remove it once this issue is resolved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions