diff --git a/docs/generators/typescript-angular.md b/docs/generators/typescript-angular.md index 6916e3f662cc..946700ad2ce1 100644 --- a/docs/generators/typescript-angular.md +++ b/docs/generators/typescript-angular.md @@ -34,6 +34,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true| |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| |stringEnums|Generate string enums instead of objects for enum values.| |false| +|subtypeVisitor|Use discriminators to create a subtype visitor.| |false| |supportsES6|Generate code that conforms to ES6.| |false| |taggedUnions|Use discriminators to create tagged unions instead of extending interfaces.| |false| |useSingleRequestParameter|Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter.| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java index e91ddecda385..e912250ebbb9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java @@ -49,6 +49,7 @@ public static enum PROVIDED_IN_LEVEL {none, root, any, platform} public static final String WITH_INTERFACES = "withInterfaces"; public static final String USE_SINGLE_REQUEST_PARAMETER = "useSingleRequestParameter"; public static final String TAGGED_UNIONS = "taggedUnions"; + public static final String SUBTYPE_VISITOR = "subtypeVisitor"; public static final String NG_VERSION = "ngVersion"; public static final String PROVIDED_IN_ROOT = "providedInRoot"; public static final String PROVIDED_IN = "providedIn"; @@ -77,6 +78,7 @@ public static enum PROVIDED_IN_LEVEL {none, root, any, platform} protected PROVIDED_IN_LEVEL providedIn = PROVIDED_IN_LEVEL.root; private boolean taggedUnions = false; + private boolean subtypeVisitor = false; public TypeScriptAngularClientCodegen() { super(); @@ -106,6 +108,9 @@ public TypeScriptAngularClientCodegen() { this.cliOptions.add(CliOption.newBoolean(TAGGED_UNIONS, "Use discriminators to create tagged unions instead of extending interfaces.", this.taggedUnions)); + this.cliOptions.add(CliOption.newBoolean(SUBTYPE_VISITOR, + "Use discriminators to create a subtype visitor.", + this.subtypeVisitor)); this.cliOptions.add(CliOption.newBoolean(PROVIDED_IN_ROOT, "Use this property to provide Injectables in root (it is only valid in angular version greater or equal to 6.0.0). IMPORTANT: Deprecated for angular version greater or equal to 9.0.0, use **providedIn** instead.", false)); @@ -200,6 +205,10 @@ public void processOpts() { taggedUnions = Boolean.parseBoolean(additionalProperties.get(TAGGED_UNIONS).toString()); } + if (additionalProperties.containsKey(SUBTYPE_VISITOR)) { + subtypeVisitor = Boolean.parseBoolean(additionalProperties.get(SUBTYPE_VISITOR).toString()); + } + if (ngVersion.atLeast("9.0.0") && additionalProperties.containsKey(PROVIDED_IN)) { setProvidedIn(additionalProperties.get(PROVIDED_IN).toString()); } else { @@ -514,11 +523,18 @@ public Map postProcessAllModels(Map objs) { CodegenModel cm = (CodegenModel) mo.get("model"); if (taggedUnions) { mo.put(TAGGED_UNIONS, true); + } + if (subtypeVisitor) { + mo.put(SUBTYPE_VISITOR, true); + } + if (taggedUnions || subtypeVisitor) { if (cm.discriminator != null && cm.children != null) { for (CodegenModel child : cm.children) { cm.imports.add(child.classname); } } + } + if (taggedUnions) { if (cm.parent != null) { cm.imports.remove(cm.parent); } diff --git a/modules/openapi-generator/src/main/resources/typescript-angular/modelGeneric.mustache b/modules/openapi-generator/src/main/resources/typescript-angular/modelGeneric.mustache index 93d0fb629db3..5e9a5b23bd21 100644 --- a/modules/openapi-generator/src/main/resources/typescript-angular/modelGeneric.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-angular/modelGeneric.mustache @@ -7,4 +7,4 @@ export interface {{classname}}{{#allParents}}{{#-first}} extends {{/-first}}{{{. {{/description}} {{#isReadOnly}}readonly {{/isReadOnly}}{{{name}}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}} | null{{/isNullable}}; {{/vars}} -}{{>modelGenericEnums}} +}{{>modelGenericEnums}}{{>modelGenericVisitor}} diff --git a/modules/openapi-generator/src/main/resources/typescript-angular/modelGenericVisitor.mustache b/modules/openapi-generator/src/main/resources/typescript-angular/modelGenericVisitor.mustache new file mode 100644 index 000000000000..1695f2b2f3c2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-angular/modelGenericVisitor.mustache @@ -0,0 +1,29 @@ +{{#subtypeVisitor}} + {{#discriminator}} + + +export interface {{classname}}Visitor { + {{#discriminator.mappedModels}} + visit{{modelName}}(value: {{modelName}}): R; + {{/discriminator.mappedModels}} +} + +export function visit{{classname}}(value: {{classname}}, visitor: {{classname}}Visitor): R { + switch (value.{{discriminator.propertyName}}) { + {{#discriminator.mappedModels}} + case '{{mappingName}}': + return visitor.visit{{modelName}}(<{{modelName}}>value); + {{/discriminator.mappedModels}} + default: + {{#taggedUnions}} + // Make the compiler fail if there is an unmapped member + const _exhaustiveCheck: never = value; + throw new Error("Unknown model discriminator for '" + value + "'" + _exhaustiveCheck); + {{/taggedUnions}} + {{^taggedUnions}} + throw new Error("Unknown model discriminator '" + value.{{discriminator.propertyName}} + "'"); + {{/taggedUnions}} + } +} + {{/discriminator}} +{{/subtypeVisitor}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-angular/modelTaggedUnion.mustache b/modules/openapi-generator/src/main/resources/typescript-angular/modelTaggedUnion.mustache index 0189ea8b0ecd..0f0304ce43f1 100644 --- a/modules/openapi-generator/src/main/resources/typescript-angular/modelTaggedUnion.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-angular/modelTaggedUnion.mustache @@ -1,5 +1,6 @@ {{#discriminator}} export type {{classname}} = {{#children}}{{^-first}} | {{/-first}}{{classname}}{{/children}}; +{{>modelGenericVisitor}} {{/discriminator}} {{^discriminator}} {{#parent}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/TypeScriptAngularClientOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/TypeScriptAngularClientOptionsProvider.java index eb8d90a468b8..8245e1ca2dd0 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/TypeScriptAngularClientOptionsProvider.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/TypeScriptAngularClientOptionsProvider.java @@ -75,6 +75,7 @@ public Map createOptions() { .put(TypeScriptAngularClientCodegen.PROVIDED_IN_ROOT, Boolean.FALSE.toString()) .put(TypeScriptAngularClientCodegen.PROVIDED_IN, PROVIDED_IN_LEVEL) .put(TypeScriptAngularClientCodegen.TAGGED_UNIONS, Boolean.FALSE.toString()) + .put(TypeScriptAngularClientCodegen.SUBTYPE_VISITOR, Boolean.FALSE.toString()) .put(TypeScriptAngularClientCodegen.NPM_REPOSITORY, NPM_REPOSITORY) .put(TypeScriptAngularClientCodegen.NG_VERSION, NG_VERSION) .put(TypeScriptAngularClientCodegen.API_MODULE_PREFIX, API_MODULE_PREFIX)