Skip to content

params always required #1778

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

Closed
1 task
RPGillespie6 opened this issue Jul 23, 2024 · 2 comments · Fixed by #1833
Closed
1 task

params always required #1778

RPGillespie6 opened this issue Jul 23, 2024 · 2 comments · Fixed by #1833
Labels
bug Something isn't working openapi-fetch Relevant to the openapi-fetch library

Comments

@RPGillespie6
Copy link

RPGillespie6 commented Jul 23, 2024

The following snippet of code should be valid, as per the README:

import createClient from "openapi-fetch";
import type { paths } from "./petstore"; // npx openapi-typescript https://petstore3.swagger.io/api/v3/openapi.yaml -o petstore.d.ts

const client = createClient<paths>();

client.POST("/store/order", {
    body: {
        id: 0,
    },
})

However, it is failing with:

error TS2345: Argument of type '{ body: {}; }' is not assignable to parameter of type '{ params: { query?: never; header?: never; path?: never; cookie?: never; }; } & { body?: { id?: number; petId?: number; quantity?: number; shipDate?: string; status?: "placed" | "approved" | "delivered"; complete?: boolean; }; } & { ...; } & Omit<...> & { ...; }'.
  Property 'params' is missing in type '{ body: {}; }' but required in type '{ params: { query?: never; header?: never; path?: never; cookie?: never; }; }'.

  6 client.POST("/store/order", {
                                ~
  7     body: {
    ~~~~~~~~~~~
...
  9     },
    ~~~~~~
 10 })
  ~

  node_modules/openapi-fetch/dist/index.d.ts:87:9
    87     : { params: T["parameters"] }
               ~~~~~~
    'params' is declared here.

If I change it to:

import createClient from "openapi-fetch";
import type { paths } from "./petstore"; // npx openapi-typescript https://petstore3.swagger.io/api/v3/openapi.yaml -o petstore.d.ts

const client = createClient<paths>();

client.POST("/store/order", {
    params: {},
    body: {
        id: 0,
    },
})

the error goes away.

Is this a recent regression? I don't remember it doing this before. I'm using typescript 5.5.4, openapi-fetch 0.10.2, and openapi-typscript 7.1.0.

my tsconfig.json:

{
    "compilerOptions": {
        "module": "ESNext",
        "moduleResolution": "node",
        "noImplicitAny": true,
    },
    "files": [
        "test.ts"
    ]
}

Checklist

@RPGillespie6 RPGillespie6 added bug Something isn't working openapi-fetch Relevant to the openapi-fetch library labels Jul 23, 2024
@RPGillespie6
Copy link
Author

RPGillespie6 commented Jul 23, 2024

I think the problem is here:

export type FindRequiredKeys<T, K extends keyof T> = K extends unknown ? (undefined extends T[K] ? never : K) : K;
/** Does this object contain required keys? */
export type HasRequiredKeys<T> = FindRequiredKeys<T, keyof T>;

A couple problems:

  1. K extends unknown seems to always be true (how can K extends unknown ever be false?)
  2. undefined extends T[K] seems to always be false (how can undefined extends T[K] ever be true?)

If I change it to this:

export type FindRequiredKeys<T, K extends keyof T> = never extends T[K] ? never : K;
/** Does this object contain required keys? */
export type HasRequiredKeys<T> = FindRequiredKeys<T, keyof T>;

It fixes it, but I'm not sure if that causes any regressions.

@ngraef
Copy link
Contributor

ngraef commented Aug 8, 2024

I tracked this down to behavioral differences based on the strictNullChecks (or strict, which includes strictNullChecks) compiler option. When that option is false (the default), undefined is considered to be a subtype of every other type except never. In that case, given this definition:

type Parameters = {
  query?: {
    name?: string;
    status?: string;
  };
  header?: never;
  path: {
    petId: number;
  };
  cookie?: never;
};

HasRequiredKeys<Parameters> will resolve to "header" | "cookie", which is clearly not the intent of that helper type. However, with strictNullChecks enabled, it correctly resolves to "path".

The problem is most noticeable in the case mentioned in the original issue, when no parameters are defined:

type Parameters = {
  query?: never;
  header?: never;
  path?: never;
  cookie?: never;
};

HasRequiredKeys<Parameters> resolves to "query" | "header" | "path" | "cookie", which then makes params a required property in the request init object.

I think we could fix this by replacing HasRequiredKeys with a helper type that doesn't rely on the inconsistent behavior of undefined extends T. I'm working on a PR that uses this helper instead:

type RequiredKeysOf<T> = {
  [K in keyof T]: {} extends Pick<T, K> ? never : K;
}[keyof T];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working openapi-fetch Relevant to the openapi-fetch library
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants