Skip to content

fix: combine accessor names and identical enum values - #3556

Open
EloB wants to merge 2 commits into
glideapps:masterfrom
EloB:fix/combine-accessor-names
Open

EloB wants to merge 2 commits into
glideapps:masterfrom
EloB:fix/combine-accessor-names

Conversation

@EloB

@EloB EloB commented Nov 15, 2026

Copy link
Copy Markdown

Description

AccessorNamesTypeAttributeKind and EnumValuesTypeAttributeKind get a
combine. Before this, both used the base class's, which panics with
Cannot combine type attribute ….

  • Accessor names (attributes/AccessorNames.ts): the result keeps every
    entry the combined copies agree on, and drops a name for which they
    have different entries, so that name falls back to its default name.
    Names that only one copy gives are kept.
  • Enum values (attributes/EnumValues.ts): copies that are all the same
    are combined. Any other combination fails as before, with the same
    message.

A test commit adds test/inputs/schema/accessors-enum-union.schema. It
has the same enum twice, once as a definition with qt-accessors and once
inline in a nullable union, which is what the TypeScript input makes of
f: E | null for a string enum. It comes with two samples and one
expected failure ("f": "c"). The commit also adds a unit test,
test/unit/enum-values-combine.test.ts: two definitions with identical
qt-enum-values combine (_A = 0,, _B = 1, in C), and two that differ
still fail with Cannot combine type attribute enumValues.

Related Issue

Fixes #3551

Motivation and Context

The TypeScript input gives each string enum's definition its member names
as qt-accessors. When the same enum also appears in a union
(E | null, E[] | F, an optional E in Python), or two enums have the
same cases, the types become one and their attributes are combined, and
quicktype stopped with Internal error: Cannot combine type attribute accessorNames.
JSON Schema definitions of the same enum with qt-accessors or
qt-enum-values failed the same way.

Why these semantics:

  • The common case is the same enum meeting itself with identical entries,
    so its member names are kept. f: E | null names the cases as f: E
    does.
  • When two copies give different names for the same value, neither is
    more right than the other. Dropping the name gives the result the
    string-literal alias form (type E = "a" | "b") gives, and the result
    does not depend on the order in which types are combined. Keeping the
    first or the last entry would depend on it.
  • Enum values cannot drop an entry the same way. A value is part of what
    the enum means, and dropping one can give two cases the same value.
    With copies {"a": "0", "b": "1"} and {"a": "1", "b": "1"}, dropping
    a gives C F_A = 1, F_B = 1 (the first case defaults to 1), and the
    duplicate case labels do not compile.

Previous Behaviour / Output

$ quicktype --src-lang typescript --lang cs e.ts
Error: Internal error: Cannot combine type attribute accessorNames.

with e.ts:

export enum E { A = "a", B = "b" }
export interface X { f: E | null }

New Behaviour / Output

The code is generated. For these four declarations, the output is
byte-for-byte the same as for the same declarations written with string-literal
aliases, in C#, Python, Go and TypeScript:

  • f: E | null
  • f: E[] | F
  • e: E; f: F, where F has the same cases as E
  • f?: E; g?: E

For the JSON Schema example with two definitions whose qt-accessors are
{a: Alpha, b: Beta} and {a: Alpha, b: Other}, the C# enum is
{ Alpha, B }. Two definitions with identical qt-enum-values now work.
Definitions whose qt-enum-values differ still fail with
Cannot combine type attribute enumValues.

No output of the repository's existing inputs changes, since combining
these attributes always failed before.

How Has This Been Tested?

Environment: macOS 26.6 (arm64), Node 24.16.0, Python 3.12.16 with
mypy 2.4.1, OpenJDK 21.0.11 with Maven 3.10.0, Ruby 4.0.6, Apple clang 21
(C, C++, Objective-C), Swift, Rust (cargo) and Dart.

On this branch:

  • npm ci, npm run build, npm run lint (passes), npm run test:unit
    (55 files, 243 tests pass).
  • QUICKTEST=true FIXTURE=schema-typescript,schema-javascript,schema-python,schema-cjson,schema-ruby,schema-java npm run test:fixtures -- test/inputs/schema/accessors-enum-union.schema:
    all 6 pass.
  • The same input with schema-swift, schema-rust, schema-dart,
    schema-typescript-zod, schema-typescript-effect-schema, schema-flow,
    schema-javascript-prop-types, schema-java-datetime-legacy,
    schema-objective-c and schema-cplusplus: all 10 pass.
  • The TypeScript-input cases above: quicktype exits with 0 for every
    --lang except objc, which fails with
    Internal error: Cannot repeat a string a negative number of times.
    The string-literal alias form fails the same way on master, so that is
    a separate problem.

With only the test commit (the fix not applied):

  • schema-typescript (and the others) fail on accessors-enum-union.schema
    with Internal error: Cannot combine type attribute accessorNames.
  • The unit test fails on the identical-values case.

Not run here, because the toolchains are not installed: C#, Go, Kotlin,
Scala, Elm, PHP, Elixir, Haskell, Crystal and Pike.

Screenshots (if appropriate):

n/a


Written by Claude (an AI assistant made by Anthropic) and posted on behalf of the account owner (EloB). The reproduction, fix and tests were checked by running them as described above.

🤖 Generated with Claude Code

EloB and others added 2 commits November 15, 2026 07:51
`accessors-enum-union.schema` has the same enum twice, as a definition
with `qt-accessors` and inline in a nullable union, which is what the
TypeScript input makes of `f: E | null` for a string enum. A unit test
covers two definitions of the same enum with `qt-enum-values`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`AccessorNamesTypeAttributeKind` and `EnumValuesTypeAttributeKind` had
no `combine`, so two types with accessor names (or enum values) that
turned out to be the same type stopped with "Internal error: Cannot
combine type attribute accessorNames." The TypeScript input gives a
string enum's definition its member names as `qt-accessors`, so string
enums in unions (`E | null`, `E[] | F`, an optional `E` in Python) and
two enums with the same values failed, as did JSON Schema definitions of
the same enum with `qt-accessors`.

Accessor names now keep the entries every copy agrees on, and drop a
name the copies disagree on, so it falls back to its default. Enum
values combine only when every copy is the same; any other combination
fails as before, because dropping a value could give two cases the same
value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant