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

React query initialData overloads don't respect the allParamsOptional config option #1564

Open
RinseV opened this issue Aug 6, 2024 · 1 comment · May be fixed by #1583
Open

React query initialData overloads don't respect the allParamsOptional config option #1564

RinseV opened this issue Aug 6, 2024 · 1 comment · May be fixed by #1583
Labels
bug Something isn't working

Comments

@RinseV
Copy link
Contributor

RinseV commented Aug 6, 2024

What are the steps to reproduce this issue?

  1. Enable the allParamsOptional option in the config
  2. Generate the hooks with the react-query client
  3. Try to use a hook with an optional parameter

OpenAPI sample spec:

openapi: '3.0.0'
info:
  version: 1.0.0
  title: Swagger Petstore
  license:
    name: MIT
servers:
  - url: http://petstore.swagger.io/v1
paths:
  /pets/{petId}:
    get:
      summary: Info for a specific pet
      operationId: pet
      tags:
        - pets
      parameters:
        - name: petId
          in: path
          required: true
          description: The id of the pet to retrieve
          schema:
            type: string
      responses:
        '200':
          description: Expected response to a valid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Pet:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        tag:
          type: string
        email:
          type: string
          format: email
        callingCode:
          type: string
          enum: ['+33', '+420', '+33'] # intentional duplicated value
        country:
          type: string
          enum: ["People's Republic of China", 'Uruguay']
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string

orval.config.ts:

import { defineConfig } from 'orval';

export default defineConfig({
  allParamsOptional: {
    output: {
      target: 'src/api/endpoints.ts',
      schemas: 'src/api/model',
      client: 'react-query',
      httpClient: 'fetch',
      allParamsOptional: true,
    },
    input: {
      target: 'petstore.yaml'
    },
  },
})

What happens?

You get a Typescript error:

No overload matches this call.
  Overload 1 of 3, '(petId: string, options?: { query?: (Partial<UseQueryOptions<petResponse, Promise<Error>, petResponse, QueryKey>> & Pick<...>) | undefined; fetch?: RequestInit | undefined; } | undefined): UseQueryResult<...> & { ...; }', gave the following error.
    Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
      Type 'undefined' is not assignable to type 'string'.
  Overload 2 of 3, '(petId: string, options?: { query?: Partial<UseQueryOptions<petResponse, Promise<Error>, petResponse, QueryKey>> | undefined; fetch?: RequestInit | undefined; } | undefined): UseQueryResult<...> & { ...; }', gave the following error.
    Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
      Type 'undefined' is not assignable to type 'string'.ts(2769)
endpoints.ts(137, 17): The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.

Generated overloads:

export function usePet<
  TData = Awaited<ReturnType<typeof pet>>,
  TError = Promise<Error>,
>(
  // Should be string | undefined | null
  petId: string,
  options: {
    query: Partial<
      UseQueryOptions<Awaited<ReturnType<typeof pet>>, TError, TData>
    > &
      Pick<
        DefinedInitialDataOptions<
          Awaited<ReturnType<typeof pet>>,
          TError,
          TData
        >,
        "initialData"
      >;
    fetch?: RequestInit;
  },
): DefinedUseQueryResult<TData, TError> & { queryKey: QueryKey };
export function usePet<
  TData = Awaited<ReturnType<typeof pet>>,
  TError = Promise<Error>,
>(
  // Should be string | undefined | null
  petId: string,
  options?: {
    query?: Partial<
      UseQueryOptions<Awaited<ReturnType<typeof pet>>, TError, TData>
    > &
      Pick<
        UndefinedInitialDataOptions<
          Awaited<ReturnType<typeof pet>>,
          TError,
          TData
        >,
        "initialData"
      >;
    fetch?: RequestInit;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey };
export function usePet<
  TData = Awaited<ReturnType<typeof pet>>,
  TError = Promise<Error>,
>(
  // Should be string | undefined | null
  petId: string,
  options?: {
    query?: Partial<
      UseQueryOptions<Awaited<ReturnType<typeof pet>>, TError, TData>
    >;
    fetch?: RequestInit;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey };
/**
 * @summary Info for a specific pet
 */

export function usePet<
  TData = Awaited<ReturnType<typeof pet>>,
  TError = Promise<Error>,
>(
  // The actual implementation has the correct types
  petId: string | undefined | null,
  options?: {
    query?: Partial<
      UseQueryOptions<Awaited<ReturnType<typeof pet>>, TError, TData>
    >;
    fetch?: RequestInit;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getPetQueryOptions(petId, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  query.queryKey = queryOptions.queryKey;

  return query;
}

What were you expecting to happen?

The optional parameter would be allowed in all overloads

Any other comments?

This PR added overloads to all hooks to allow for proper typing of the response when supplying initialData. However, these overloads don't consider the allParamsOptional option for the path parameters resulting in 3 function signatures that don't allow for undefined path parameters while the actual implementation does allow for it.

The problem is that these function overloads use the definition of the endpoint rather than the implementation and the allParamsOptional config setting only applies to the implementation of the endpoint's definition. However, the function overloads cannot use the implementation because the implementation also includes the default values which you cannot define in function overloads. We would need something that's in between the definition and implementation that does include the | undefined | null from the allParamsOptional setting but does not include the default parameters from the implementation.

What versions are you using?

System:
    OS: Linux 5.15 Ubuntu 20.04.6 LTS (Focal Fossa)
    CPU: (16) x64 AMD Ryzen 7 5800X 8-Core Processor
    Memory: 5.14 GB / 7.76 GB
    Container: Yes
    Shell: 5.8 - /usr/bin/zsh
  npmPackages:
    @tanstack/react-query: ^5.51.21 => 5.51.21 
    orval: ^7.0.1 => 7.0.1
@melloware
Copy link
Collaborator

PR is welcome.

cc @lnkarma

@melloware melloware added the bug Something isn't working label Aug 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants