Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REQ] typescript-fetch-generator: Add helper function which returns an instance of an "oneOf" #18059

Closed
bamkrs opened this issue Mar 8, 2024 · 1 comment · Fixed by #18694

Comments

@bamkrs
Copy link

bamkrs commented Mar 8, 2024

Is your feature request related to a problem? Please describe.

Imagine this schema

"Schedule": {
  "oneOf": [
    {
      "type": "object",
      "required": [
        "Yearly"
      ],
      "properties": {
        "Yearly": {
          "$ref": "#/components/schemas/YearlySchedule"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "Monthly"
      ],
      "properties": {
        "Monthly": {
          "$ref": "#/components/schemas/MonthlySchedule"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "Weekly"
      ],
      "properties": {
        "Weekly": {
          "$ref": "#/components/schemas/WeeklySchedule"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "Daily"
      ],
      "properties": {
        "Daily": {
          "$ref": "#/components/schemas/DailySchedule"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "Once"
      ],
      "properties": {
        "Once": {
          "type": "string",
          "format": "date-time",
          "description": "execute once on the given date and then never again"
        }
      }
    }
  ],
  "description": "..."
},

In typescript-fetch, this gets generated to multiple "oneOf". The one Schedule having the "Yearly" property is translated to the following class:

export interface ScheduleOneOf {
    /**
     * 
     * @type {YearlySchedule}
     * @memberof ScheduleOneOf
     */
    Yearly: YearlySchedule;
}

/**
 * Check if a given object implements the ScheduleOneOf interface.
 */
export function instanceOfScheduleOneOf(value: object): boolean {
    if (!('Yearly' in value)) return false;
    return true;
}

export function ScheduleOneOfFromJSON(json: any): ScheduleOneOf {
    return ScheduleOneOfFromJSONTyped(json, false);
}

export function ScheduleOneOfFromJSONTyped(json: any, ignoreDiscriminator: boolean): ScheduleOneOf {
    if (json === undefined || json === null) {
        return json;
    }
    return {
        
        'Yearly': YearlyScheduleFromJSON(json['Yearly']),
    };
}

export function ScheduleOneOfToJSON(value?: ScheduleOneOf | null): any {
    if (value === undefined) {
        return undefined;
    }
    if (value === null) {
        return null;
    }
    return {
        
        'Yearly': YearlyScheduleToJSON(value['Yearly']),
    };
}

Working with these oneOf is extremly frustrating, since I have to run a huge if else blocks to figure out which oneOf it actually is and have to parse it. This is one very simple example:

  const getHourOfSchedule = (i: Schedule): number => {
    if (instanceOfScheduleOneOf(i) && (i as ScheduleOneOf).Yearly !== undefined) {
      return (i as ScheduleOneOf).Yearly.hour;
    } else if (instanceOfScheduleOneOf1(i) && (i as ScheduleOneOf1).Monthly !== undefined) {
      return (i as ScheduleOneOf).Monthly.hour;
    } else if (instanceOfScheduleOneOf2(i) && (i as ScheduleOneOf2).Weekly !== undefined) {
      return (i as ScheduleOneOf).Weekly.hour;
    } else if (instanceOfScheduleOneOf3(i) && (i as ScheduleOneOf3).Daily !== undefined) {
      return (i as ScheduleOneOf).Daily.hour;
    } else if (instanceOfScheduleOneOf4(i) && (i as ScheduleOneOf4).Once !== undefined) {
      return  (i as ScheduleOneOf).Once.hour;
    }
    return -1;
  }

Describe the solution you'd like

If there was just one simple helper function in each oneOf class like the following:

const asScheduleOneOf = (oneOf: Schedule): ScheduleOneOf | null => {
  if (instanceOfScheduleOneOf(oneOf) && (oneOf as ScheduleOneOf).Yearly !== undefined) {
    return oneOf as ScheduleOneOf;
  }
  return null;
}

or in "pseudo code"

const as{{classname}} = (oneOf: {{superclassname}}): {{classname}} | null => {
  if (instanceOf{{classname}}(oneOf) && <for each required property (oneOf as {{classname}}).{{property}} !== undefined)> {
    return oneOf as {{classname}};
  }
  return null;
}

my life would be so much easier and I need to write so much less code.

Describe alternatives you've considered

As an alternative the already existing instanceOfScheduleOneOf isn't much of help here. Since it only check if the interface is satisfied and not if the required values aren't undefined.

@bamkrs
Copy link
Author

bamkrs commented Mar 8, 2024

Furthermore:

export function instanceOfScheduleOneOf(value: object): boolean {
    if (!('Yearly' in value)) return false;
    return true;
}

Is completely useless/broken.

If I use typescript-fetch to get an array of these Schedules like

api.schedules.getSchedules().then((schedules: Array<Schedule>) => {
  console.log(schedules);
});

I receive the following:

[
  {
    Yearly: {Day: 1, Month: 1, Hour: 1, Minute: 0},
    Monthly: undefined,
    Weekly: undefined,
    Daily: undefined,
    Once: undefined
  },
  {
    Yearly: undefined,
    Monthly: undefined,
    Weekly: {WeekDay: 'Sun', Hour: 1, Minute: 0},
    Daily: undefined,
    Once: undefined
  }
]

Now I update the second schedule to

  {
    Yearly: undefined,
    Monthly: undefined,
    Weekly: {WeekDay: 'Mon', Hour: 1, Minute: 0},
    Daily: undefined,
    Once: undefined
  }

and pass this to ScheduleToJSON inside the update call auto-generated by typescript-fetch, I will allways receive {} since instanceOfScheduleOneOf checks for 'Yearly' in value - which is true. Now it gets passed to ScheduleOneOfToJSON which tries to

return {
    'Yearly': YearlyScheduleToJSON(value['Yearly']),
};

which obviously just returns {'Yearly': undefined}, which finally ends up being {} in my update request.

To fix this, I have to run it through an extra function:

export const extractSetSchedule = (schedule: Schedule): Schedule => {
  if (instanceOfScheduleOneOf(schedule) && (schedule as ScheduleOneOf).Yearly !== undefined) {
    return {Yearly: (schedule as ScheduleOneOf).Yearly};
  } else if (instanceOfScheduleOneOf1(schedule) && (schedule as ScheduleOneOf1).Monthly !== undefined) {
    return {Monthly: (schedule as ScheduleOneOf1).Monthly};
  } else if (instanceOfScheduleOneOf2(schedule) && (schedule as ScheduleOneOf2).Weekly !== undefined) {
    return {Weekly: (schedule as ScheduleOneOf2).Weekly};
  } else if (instanceOfScheduleOneOf3(schedule) && (schedule as ScheduleOneOf3).Daily !== undefined) {
    return {Daily: (schedule as ScheduleOneOf3).Daily};
  } else if (instanceOfScheduleOneOf4(schedule) && (schedule as ScheduleOneOf4).Once !== undefined) {
    return {Once: (schedule as ScheduleOneOf4).Once};
  }
  return schedule;
}

which is really cumbersome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant