Skip to content

Commit 451f488

Browse files
Typescript OneOf: Find matching type if OneClass has no discriminator
1 parent 0e6998f commit 451f488

6 files changed

Lines changed: 45 additions & 10 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ public TypeScriptClientCodegen() {
174174
// models
175175
setModelPackage("models");
176176
supportingFiles.add(new SupportingFile("model" + File.separator + "ObjectSerializer.mustache", "models", "ObjectSerializer.ts"));
177-
supportingFiles.add(new SupportingFile("model" + File.separator + "OneOfInterface.mustache", "models", "OneOfInterface.ts"));
177+
supportingFiles.add(new SupportingFile("model" + File.separator + "OneOfClass.mustache", "models", "OneOfClass.ts"));
178+
178179
modelTemplateFiles.put("model" + File.separator + "model.mustache", ".ts");
179180

180181
// api

modules/openapi-generator/src/main/resources/typescript/model/ObjectSerializer.mustache

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,15 @@ export class ObjectSerializer {
128128
// Check the discriminator
129129
let discriminatorProperty = typeMap[expectedType].discriminator;
130130
if (discriminatorProperty == null) {
131-
return expectedType; // the type does not have a discriminator. use it.
131+
if (this.hasFindMatchingTypeMethod(typeMap[expectedType])) {
132+
const foundType = typeMap[expectedType].findMatchingType(data);
133+
if (foundType == undefined) {
134+
throw new Error("Unable to determine a unique type for the provided object: oneOf type resolution failed. The object does not match exactly one schema. Consider adding a discriminator or making schemas mutually exclusive.");
135+
}
136+
137+
return foundType;
138+
}
139+
return expectedType; // the type does not have a discriminator and findMatchingType method. use it.
132140
} else {
133141
if (data[discriminatorProperty]) {
134142
var discriminatorType = data[discriminatorProperty];
@@ -147,6 +155,13 @@ export class ObjectSerializer {
147155
}
148156
}
149157
158+
public static hasFindMatchingTypeMethod(klass: any): boolean {
159+
if (typeof klass.findMatchingType === 'function') {
160+
return true;
161+
}
162+
return false;
163+
}
164+
150165
public static serialize(data: any, type: string, format: string): any {
151166
if (data == undefined) {
152167
return data;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export class OneOfClass {
2+
public static instanceOf(data: any, attributeTypeMap: Array<{name: string, baseName: string, type: string, format: string, required: boolean}>): boolean {
3+
for(const attribute of attributeTypeMap) {
4+
if (attribute.required) {
5+
if (!(attribute.baseName in data) || data[attribute.baseName] === undefined) {
6+
return false;
7+
}
8+
}
9+
}
10+
11+
return true;
12+
}
13+
}

modules/openapi-generator/src/main/resources/typescript/model/OneOfInterface.mustache

Lines changed: 0 additions & 3 deletions
This file was deleted.

modules/openapi-generator/src/main/resources/typescript/model/model.mustache

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { {{classname}} } from '{{filename}}{{importFileExtension}}';
66
{{/tsImports}}
77
{{#oneOf}}
88
{{#-first}}
9-
import { OneOfInterface } from '../models/OneOfInterface{{importFileExtension}}'
9+
import { OneOfClass } from '../models/OneOfClass{{importFileExtension}}';
1010
{{/-first}}
1111
{{/oneOf}}
1212
{{^oneOf}}
@@ -53,13 +53,14 @@ export class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{
5353
{{/hasDiscriminatorWithNonEmptyMapping}}
5454

5555
{{^isArray}}
56-
static {{#parent}}override {{/parent}}readonly attributeTypeMap: Array<{name: string, baseName: string, type: string, format: string}> = [
56+
static {{#parent}}override {{/parent}}readonly attributeTypeMap: Array<{name: string, baseName: string, type: string, format: string, required: boolean}> = [
5757
{{#vars}}
5858
{
5959
"name": "{{name}}",
6060
"baseName": "{{baseName}}",
6161
"type": "{{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}",
62-
"format": "{{dataFormat}}"
62+
"format": "{{dataFormat}}",
63+
"required": {{required}}
6364
}{{^-last}},
6465
{{/-last}}
6566
{{/vars}}

modules/openapi-generator/src/main/resources/typescript/model/modelOneOf.mustache

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type {{classname}} = {{#oneOf}}{{{.}}}{{^-last}} | {{/-last}}{{/oneOf}};
1010
* {{{.}}}{{/description}}
1111
* @export
1212
*/
13-
export class {{classname}}Class implements OneOfInterface {
13+
export class {{classname}}Class extends OneOfClass {
1414
{{#discriminator}}
1515
static readonly discriminator: string | undefined = "{{discriminatorName}}";
1616
{{/discriminator}}
@@ -30,7 +30,15 @@ export class {{classname}}Class implements OneOfInterface {
3030
static readonly mapping: {[index: string]: string} | undefined = undefined;
3131
{{/hasDiscriminatorWithNonEmptyMapping}}
3232

33-
findMatchingType(): string | undefined {
33+
static readonly arrayOfTypes: Array<{{#oneOf}}typeof {{{.}}}{{^-last}} | {{/-last}}{{/oneOf}}> = [{{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}];
34+
35+
public static findMatchingType(data:any): string | undefined {
36+
for(const type of this.arrayOfTypes) {
37+
if (this.instanceOf(data, type.getAttributeTypeMap())) {
38+
return type.name;
39+
}
40+
}
41+
3442
return undefined;
3543
}
3644
}

0 commit comments

Comments
 (0)