Skip to content

Support field names which aren't legal typescript identifiers. - #24741

Open
brendandburns wants to merge 1 commit into
OpenAPITools:masterfrom
brendandburns:master
Open

Support field names which aren't legal typescript identifiers.#24741
brendandburns wants to merge 1 commit into
OpenAPITools:masterfrom
brendandburns:master

Conversation

@brendandburns

@brendandburns brendandburns commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

See:

kubernetes-client/javascript#2962


Summary by cubic

Preserves fields whose JSON names aren’t legal TypeScript identifiers by falling back to the field’s baseName during serialization. Previously, ObjectSerializer only read attributeType.name, which dropped values stored under baseName.

  • Updates typescript-node/models.mustache and typescript/model/ObjectSerializer.mustache to use data[name] when it’s an own property; otherwise use data[baseName]. Samples and one test expectation updated accordingly.
  • Behavior: if both name and baseName exist, name wins. Uses own-property check to avoid prototype pollution. Deserialization is unchanged.
  • Compatibility: no migration required; generated clients now serialize objects with illegal TS identifier fields correctly.

Written for commit 8505f22. Summary will update on new commits.

Review in cubic

- Add custom TLS server name support and update generated samples.\n- Fix the missing declaration.\n- Preserve unmapped baseName fields during TypeScript serialization.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 23 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/openapi3/client/petstore/typescript/builds/nullable-enum/models/ObjectSerializer.ts">

<violation number="1" location="samples/openapi3/client/petstore/typescript/builds/nullable-enum/models/ObjectSerializer.ts:183">
P2: When a schema contains a `constructor` field that is absent from the input, this fallback reads inherited `data.constructor` after `_constructor` is missing. Check that `baseName` is also an own property before reading it, so absent fields remain `undefined`.</violation>
</file>

<file name="samples/openapi3/client/petstore/typescript/builds/composed-schemas/models/ObjectSerializer.ts">

<violation number="1" location="samples/openapi3/client/petstore/typescript/builds/composed-schemas/models/ObjectSerializer.ts:204">
P2: When an optional schema property is named `constructor` and has an array type, an unset model reads inherited `data.constructor` here and serialization throws because functions are not iterable. Check `baseName` ownership before falling back so missing optional fields remain `undefined`.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/typescript/model/ObjectSerializer.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/typescript/model/ObjectSerializer.mustache:208">
P2: When the same object carries both the sanitized `name` key and the raw `baseName` key with different values, this reads `data[name]` first and silently drops the `baseName` value. That happens with property-name collisions, which the generator produces: a legal field `foo_bar` gets `name = "foo_bar"`, while an illegal field `foo-bar` is sanitized to the same `name = "foo_bar"` (see `toVarName`/`sanitizeName` in `TypeScriptClientCodegen`). The lookup on `attributeType.name` is then satisfied by the other field's value and serialized under `foo-bar`, emitting incorrect data. Since `serialize` outputs under `attributeType.baseName`, prefer the `baseName` key and fall back to `name`, which also matches the wire-format key and both file patterns for consistency.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type, attributeType.format);
const value = Object.prototype.hasOwnProperty.call(data, attributeType.name)
? data[attributeType.name]
: data[attributeType.baseName];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a schema contains a constructor field that is absent from the input, this fallback reads inherited data.constructor after _constructor is missing. Check that baseName is also an own property before reading it, so absent fields remain undefined.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/client/petstore/typescript/builds/nullable-enum/models/ObjectSerializer.ts, line 183:

<comment>When a schema contains a `constructor` field that is absent from the input, this fallback reads inherited `data.constructor` after `_constructor` is missing. Check that `baseName` is also an own property before reading it, so absent fields remain `undefined`.</comment>

<file context>
@@ -178,7 +178,10 @@ export class ObjectSerializer {
-                instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type, attributeType.format);
+                const value = Object.prototype.hasOwnProperty.call(data, attributeType.name)
+                    ? data[attributeType.name]
+                    : data[attributeType.baseName];
+                instance[attributeType.baseName] = ObjectSerializer.serialize(value, attributeType.type, attributeType.format);
             }
</file context>
Suggested change
: data[attributeType.baseName];
: Object.prototype.hasOwnProperty.call(data, attributeType.baseName)
? data[attributeType.baseName]
: undefined;

instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type, attributeType.format);
const value = Object.prototype.hasOwnProperty.call(data, attributeType.name)
? data[attributeType.name]
: data[attributeType.baseName];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When an optional schema property is named constructor and has an array type, an unset model reads inherited data.constructor here and serialization throws because functions are not iterable. Check baseName ownership before falling back so missing optional fields remain undefined.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/client/petstore/typescript/builds/composed-schemas/models/ObjectSerializer.ts, line 204:

<comment>When an optional schema property is named `constructor` and has an array type, an unset model reads inherited `data.constructor` here and serialization throws because functions are not iterable. Check `baseName` ownership before falling back so missing optional fields remain `undefined`.</comment>

<file context>
@@ -199,7 +199,10 @@ export class ObjectSerializer {
-                instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type, attributeType.format);
+                const value = Object.prototype.hasOwnProperty.call(data, attributeType.name)
+                    ? data[attributeType.name]
+                    : data[attributeType.baseName];
+                instance[attributeType.baseName] = ObjectSerializer.serialize(value, attributeType.type, attributeType.format);
             }
</file context>

Comment on lines +208 to +210
const value = Object.prototype.hasOwnProperty.call(data, attributeType.name)
? data[attributeType.name]
: data[attributeType.baseName];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When the same object carries both the sanitized name key and the raw baseName key with different values, this reads data[name] first and silently drops the baseName value. That happens with property-name collisions, which the generator produces: a legal field foo_bar gets name = "foo_bar", while an illegal field foo-bar is sanitized to the same name = "foo_bar" (see toVarName/sanitizeName in TypeScriptClientCodegen). The lookup on attributeType.name is then satisfied by the other field's value and serialized under foo-bar, emitting incorrect data. Since serialize outputs under attributeType.baseName, prefer the baseName key and fall back to name, which also matches the wire-format key and both file patterns for consistency.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/typescript/model/ObjectSerializer.mustache, line 208:

<comment>When the same object carries both the sanitized `name` key and the raw `baseName` key with different values, this reads `data[name]` first and silently drops the `baseName` value. That happens with property-name collisions, which the generator produces: a legal field `foo_bar` gets `name = "foo_bar"`, while an illegal field `foo-bar` is sanitized to the same `name = "foo_bar"` (see `toVarName`/`sanitizeName` in `TypeScriptClientCodegen`). The lookup on `attributeType.name` is then satisfied by the other field's value and serialized under `foo-bar`, emitting incorrect data. Since `serialize` outputs under `attributeType.baseName`, prefer the `baseName` key and fall back to `name`, which also matches the wire-format key and both file patterns for consistency.</comment>

<file context>
@@ -205,7 +205,10 @@ export class ObjectSerializer {
             let instance: {[index: string]: any} = {};
             for (let attributeType of attributeTypes) {
-                instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type, attributeType.format);
+                const value = Object.prototype.hasOwnProperty.call(data, attributeType.name)
+                    ? data[attributeType.name]
+                    : data[attributeType.baseName];
</file context>
Suggested change
const value = Object.prototype.hasOwnProperty.call(data, attributeType.name)
? data[attributeType.name]
: data[attributeType.baseName];
const value = Object.prototype.hasOwnProperty.call(data, attributeType.baseName)
? data[attributeType.baseName]
: data[attributeType.name];

@wing328

wing328 commented Aug 20, 2026

Copy link
Copy Markdown
Member

thanks for the PR

cc @TiFu (2017/07) @taxpon (2017/07) @sebastianhaas (2017/07) @kenisteward (2017/07) @Vrolijkx (2017/09) @macjohnny (2018/01) @topce (2018/10) @akehir (2019/07) @petejohansonxo (2019/11) @amakhrov (2020/02) @davidgamero (2022/03) @mkusaka (2022/04) @joscha (2024/10) @KannaKim (2026/07)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants