Description
Is your feature request related to a problem? Please describe.
Typescript codegen does not apply open api discriminator as string constant in related objects.
For example, in the following open api file
openapi: 3.0.0
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
paths:
/pets/{id}:
get:
parameters:
- name: id
required: true
in: path
schema:
type: string
responses:
200:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
discriminator:
propertyName: petType
mapping:
Dog: '#/components/schemas/Dog'
Cat: '#/components/schemas/Cat'
Cat:
type: object
properties:
petType:
type: string
cat_exclusive:
type: string
Dog:
type: object
properties:
petType:
type: string
dog_exclusive:
type: string
Output is
schema {
query: Query
}
directive @oneOf on OBJECT | INTERFACE
directive @discriminator(field: String) on INTERFACE | UNION
directive @globalOptions(sourceName: String, endpoint: String, operationHeaders: ObjMap, queryStringOptions: ObjMap, queryParams: ObjMap) on OBJECT
directive @httpOperation(path: String, operationSpecificHeaders: ObjMap, httpMethod: HTTPMethod, isBinary: Boolean, requestBaseBody: ObjMap, queryParamArgMap: ObjMap, queryStringOptionsByParam: ObjMap) on FIELD_DEFINITION
type Query @globalOptions(sourceName: "Pet") {
pets_by_id(id: String!): Pet @httpOperation(path: "/pets/{args.id}", operationSpecificHeaders: "{\\"accept\\":\\"application/json\\"}", httpMethod: GET)
}
union Pet @discriminator(field: "petType") = Cat | Dog
type Cat {
petType: String
cat_exclusive: String
}
type Dog {
petType: String
dog_exclusive: String
}
scalar ObjMap
enum HTTPMethod {
GET
HEAD
POST
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
}
Which is correct, but then, when generating types, the discriminator is completely ignored
export type Pet = Cat | Dog;
export type Cat = {
petType?: Maybe<Scalars['String']>;
cat_exclusive?: Maybe<Scalars['String']>;
};
export type Dog = {
petType?: Maybe<Scalars['String']>;
dog_exclusive?: Maybe<Scalars['String']>;
};
This way we lost the ability to narrow Pet down to Cat or Dog using its petType property.
One alternative that fixes the issue is to declare the properties as constants like the following:
openapi: 3.0.0
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
paths:
/pets/{id}:
get:
parameters:
- name: id
required: true
in: path
schema:
type: string
responses:
200:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
discriminator:
propertyName: petType
mapping:
Dog: '#/components/schemas/Dog'
Cat: '#/components/schemas/Cat'
Cat:
type: object
properties:
petType:
type: string
enum: ['Cat']
cat_exclusive:
type: string
Dog:
type: object
properties:
petType:
type: string
enum: ['Dog']
dog_exclusive:
type: string
Then, we get something like the following in schema
type Cat {
petType: Cat_const
cat_exclusive: String
}
enum Cat_const @typescript(type: "\"Cat\"") @example(value: "\"Cat\"") {
Cat @enum(value: "\"Cat\"")
}
And types are correct
export type Pet = Cat | Dog;
export type Cat = {
petType?: Maybe<Cat_const>;
cat_exclusive?: Maybe<Scalars['String']>;
};
export type Cat_const =
| 'Cat';
The issue with that approach is that we will now have conflicting constants in our unions in graphql.
Describe the solution you'd like
I would like one of the following solutions
- Discriminated items should be tagged with some kind of directive in graphql schema. They should still be a String, but TS codegen should use the directive to resolve the types correctly.
- A flag to tell the schema generator to threat enums as strings (not as clean as first one, but based on the current workaround we use)
Describe alternatives you've considered
We currently use the enum solution, but then we do some post-processing in the schemas and replace all constants with strings so we dont face the problem with conflicting types. Thats not a nice solution tho as we are manipulating generated code and if we want to use constants at some point, we wont be able to.