OpenAPITools / OpenAPITools/openapi-generator
[BUG] [typescript-fetch] openapi v2 `discriminator` with `allOf` not generating desired type
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator (example)?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
using openapi v2 discriminator with allOfdoesn't generate desired model types.
https://swagger.io/specification/v2/
openapi-generator version
v5.2.1 (same in the current latest master)
OpenAPI declaration file content or url
swagger: "2.0"
info:
description: bug test
version: "1.0.0"
title: bug test
schemes:
- "https"
- "http"
produces:
- application/json
consumes:
- application/json
paths:
/values:
get:
tags:
- "value"
operationId: "getValues"
responses:
"200":
description: ok
schema:
type: array
items:
$ref: "#/definitions/Value"
definitions:
Value:
type: object
discriminator: valueType
properties:
name:
type: string
valueType:
type: string
required:
- name
- valueType
Datetime:
description: A representation of a datetime
allOf:
- $ref: '#/definitions/Value'
- type: object
properties:
value:
type: string
format: 'date-time'
datetimy:
type: boolean
required:
- value
Int:
description: A representation of a int
allOf:
- $ref: '#/definitions/Value'
- type: object
properties:
value:
type: integer
inty:
type: boolean
required:
- value
Generation Details
openapi-generator generate \
-g typescript-fetch
-p typescriptThreePlus=true,supportsES6=true
-i swagger.yml
-o ./out
(adding - p legacyDiscriminatorBehavior=false doesn't really change anything)
Steps to reproduce
run the command above
expected behavior
api response has the field .value with the intended type
const api = new ValueApi()
const values = api.getValues()
if (values[0].valueType === 'Int') {
let intValue number
intValue = values[0].value // <= works without error, and preferably has number type after `if (values[0].valueType === 'Int') `
}
actual behavior
.value field doesn' exist
...
intValue = values[0].value // => Property 'value' does not exist on type 'Value'.
actual output
// ----- apis/ValueApi.ts
export class ValueApi extends runtime.BaseAPI {
// ...
async getValuesRaw(): Promise<runtime.ApiResponse<Array<Value>>> {
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/values`,
method: 'GET',
headers: headerParameters,
query: queryParameters,
});
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(ValueFromJSON));
}
// ----- models/Value.ts
export interface Value {
/**
*
* @type {string}
* @memberof Value
*/
name: string;
/**
*
* @type {string}
* @memberof Value
*/
valueType: string;
}
export function ValueFromJSON(json: any): Value {
return ValueFromJSONTyped(json, false);
}
export function ValueFromJSONTyped(json: any, ignoreDiscriminator: boolean): Value {
if ((json === undefined) || (json === null)) {
return json;
}
if (!ignoreDiscriminator) {
if (json['valueType'] === 'Datetime') {
return DatetimeFromJSONTyped(json, true);
}
if (json['valueType'] === 'Int') {
return IntFromJSONTyped(json, true);
}
}
return {
'name': json['name'],
'valueType': !exists(json, 'valueType') ? undefined : json['valueType'],
};
}
// ----- models/Datetime.ts
export interface Datetime extends Value {
/**
*
* @type {Date}
* @memberof Datetime
*/
value: Date;
/**
*
* @type {boolean}
* @memberof Datetime
*/
datetimy?: boolean;
}
// ----- models/Int.ts
export interface Int extends Value {
/**
*
* @type {number}
* @memberof Int
*/
value: number;
/**
*
* @type {boolean}
* @memberof Int
*/
inty?: boolean;
}
Related issues/PRs
probably none
Suggest a fix
use something like discriminated union in tyepscript
Proof of concept:
interface int {
valueType: "Int"
value: number
}
interface datetime {
valueType: "Datetime"
value: Date
}
type value = int | datetime
function getValue(): value[] {
return [
{
value: 1,
valueType: 'Int',
},
{
value: new Date(),
valueType: 'Datetime',
},
]
}
getValue().map(v => {
if (v.valueType === 'Int') {
let intv: number
intv = v.value
} else if (v.valueType === 'Datetime') {
let datev: Date
datev = v.value
}
})
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the issue with the provided Swagger 2.0 declaration and the typescript-fetch generation command. Inspect the generated apis/ValueApi.ts and models/Value.ts, along with Datetime.ts and Int.ts, to compare the base Value type with the allOf models. Done means the generated response exposes the subtype value and TypeScript narrows it correctly from valueType.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100