hey-api / openapi-ts

✨ Turn your OpenAPI specification into a beautiful TypeScript client
https://heyapi.vercel.app
Other
1k stars 83 forks source link

Discriminator in OpenApi schema not recognized correctly #993

Open markosandalj opened 2 weeks ago

markosandalj commented 2 weeks ago

Description

Given this OpenApi schema yaml

components:
  schemas:
    FrontofficePageBlock:
      oneOf:
      - $ref: '#/components/schemas/FrontofficePageBlockLinkWithIconBlockList'
      - $ref: '#/components/schemas/FrontofficePageBlockCourseListingBlock'
      discriminator:
        propertyName: type
        mapping:
          link_with_icon_list: '#/components/schemas/FrontofficePageBlockLinkWithIconBlockList'
          course_listing: '#/components/schemas/FrontofficePageBlockCourseListingBlock'
    FrontofficePageBlockCourseListingBlock:
      allOf:
      - $ref: '#/components/schemas/FrontofficePageBlockShared'
      - $ref: '#/components/schemas/CourseListingBlock'
    FrontofficePageBlockLinkWithIconBlockList:
      allOf:
      - $ref: '#/components/schemas/FrontofficePageBlockShared'
      - $ref: '#/components/schemas/LinkWithIconBlockList'
    FrontofficePageBlockShared:
      type: object
      properties:
        id:
          type: string
        type:
          $ref: '#/components/schemas/FrontofficePageBlockSharedTypeEnum'
      required:
      - id
      - type
    FrontofficePageBlockSharedTypeEnum:
      enum:
      - link_with_icon_list
      - course_listing
      type: string

I would expect the generated types to look like this

export type FrontofficePageBlock =
    | ({ type: 'course_listing' } & FrontofficePageBlockCourseListingBlock)
    | ({ type: 'link_with_icon_list' } & FrontofficePageBlockLinkWithIconBlockList)

but this is what we are getting

export type FrontofficePageBlock = FrontofficePageBlockLinkWithIconBlockList | FrontofficePageBlockCourseListingBlock ;

export type FrontofficePageBlockCourseListingBlock = FrontofficePageBlockShared & CourseListingBlock;
export type FrontofficePageBlockLinkWithIconBlockList = FrontofficePageBlockShared & LinkWithIconBlockList;

export type FrontofficePageBlockShared = {
    id: string;
    type: FrontofficePageBlockSharedTypeEnum;
};

export enum FrontofficePageBlockSharedTypeEnum {
    LINK_WITH_ICON_LIST = 'link_with_icon_list',
    COURSE_LISTING = 'course_listing',
}

At first it seems like types are correct but when trying to narrow the block type inside a switch statement by field "type" it is impossible since each block is not strictly connected to its "type" field.

An example:


switch (block.type) {
    case FrontofficePageBlockSharedTypeEnum.COURSE_LISTING:
        // typescript doesn't recognize this as FrontofficePageBlockCourseListingBlock but as FrontofficePageBlock
        ...
        break
    case FrontofficePageBlockSharedTypeEnum.LINK_WITH_ICON_LIST:
        // typescript doesn't recognize this as FrontofficePageBlockLinkWithIconBlockList but as FrontofficePageBlock
        ...
        break
}

It seems like the generator is ignoring the discriminator field from open api schema.

Reproducible example or configuration

https://stackblitz.com/edit/hey-api-client-fetch-example-emxls6?file=src%2FApp.tsx

OpenAPI specification (optional)

openapi: 3.0.3
info:
  title: Bug API
  version: 1.0.0
  description: API endpoints for bug
components:
  schemas:
    CourseListingBlock:
      type: object
      properties:
        foo:
          type: string
        id:
          type: string
      required:
      - id
      - foo
    LinkWithIconBlockList:
      type: object
      properties:      
        bar:
          type: string    
        id:
          type: string
      required:
      - id
      - bar
    FrontofficePageBlock:
      oneOf:
      - $ref: '#/components/schemas/FrontofficePageBlockLinkWithIconBlockList'
      - $ref: '#/components/schemas/FrontofficePageBlockCourseListingBlock'
      discriminator:
        propertyName: type
        mapping:
          link_with_icon_list: '#/components/schemas/FrontofficePageBlockLinkWithIconBlockList'
          course_listing: '#/components/schemas/FrontofficePageBlockCourseListingBlock'
    FrontofficePageBlockCourseListingBlock:
      allOf:
      - $ref: '#/components/schemas/FrontofficePageBlockShared'
      - $ref: '#/components/schemas/CourseListingBlock'
    FrontofficePageBlockLinkWithIconBlockList:
      allOf:
      - $ref: '#/components/schemas/FrontofficePageBlockShared'
      - $ref: '#/components/schemas/LinkWithIconBlockList'
    FrontofficePageBlockShared:
      type: object
      properties:
        id:
          type: string
        type:
          $ref: '#/components/schemas/FrontofficePageBlockSharedTypeEnum'
      required:
      - id
      - type
    FrontofficePageBlockSharedTypeEnum:
      enum:
      - link_with_icon_list
      - course_listing
      type: string

System information (optional)

No response

stackblitz[bot] commented 2 weeks ago

Fix this issue in StackBlitz Codeflow Start a new pull request in StackBlitz Codeflow.

mrlubos commented 2 weeks ago

@markosandalj I think that sounds right, discriminators aren't supported right now. Will need to add