ctimmerm / axios-mock-adapter

Axios adapter that allows to easily mock requests
MIT License
3.47k stars 245 forks source link

TypeScript error when axios instance is created from an import that uses `axios >=1.6.0`'s ESM types #400

Open evelynhathaway opened 1 month ago

evelynhathaway commented 1 month ago

Overview

With axios@1.7.7, axios-mock-adapter@2.1.0, typescript@5.6.3, using MockAdapter causes TypeScript to error about type differences between axios when imported via our ESM code and the types imported by axios-mock-adapter.

The newest axios version that this does not occur is axios@1.5.1, the type change seems to be introduced in axios@1.6.0. However, I don't believe it's an axios issue, or at least one that's as straightforward as you may expect. axios's types ESM and CJS are composed of the same literals and unions for AxiosRequestHeaders, so I feel like TypeScript is providing an unhelpful error about assignability of members of what is functionally the same union. The crux is that the classes AxiosHeaders (ESM) and axios.AxiosHeaders (CJS) are not assignable.

Besides downgrading to axios@1.5.1, this also doesn't occur when using .cts or "type": "script". Unfortunately downgrading to a vulnerable axios version and rewriting our modules to CommonJS are not feasible workarounds for my team, so we're as any'ing the errors away.

Possible Fix

It appears that axios-retry fixed this assignability issue occurring from the different type files by using ESM or CJS axios types based on whether axios-retry was imported from ESM or CJS: https://github.com/softonic/axios-retry/issues/159#issuecomment-1809406232. Perhaps this approach would work for axios-mock-adapter?

CodeSandbox

https://codesandbox.io/p/devbox/yxnj2p?file=%2Fsrc%2Findex.ts%3A6%2C1

Error

Argument of type 'import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).AxiosInstance' is not assignable to parameter of type 'import("/project/workspace/node_modules/axios/index").AxiosInstance'.
  Types of property 'defaults' are incompatible.
    Type 'Omit<import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).AxiosDefaults<any>, "headers"> & { headers: import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).HeadersDefaults & { ...; }; }' is not assignable to type 'Omit<import("/project/workspace/node_modules/axios/index").AxiosDefaults<any>, "headers"> & { headers: import("/project/workspace/node_modules/axios/index").HeadersDefaults & { ...; }; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
      Type 'Omit<AxiosDefaults<any>, "headers"> & { headers: HeadersDefaults & { [key: string]: AxiosHeaderValue; }; }' is not assignable to type 'Omit<AxiosDefaults<any>, "headers">' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
        Types of property 'transformRequest' are incompatible.
          Type 'import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).AxiosRequestTransformer | import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).AxiosRequestTransformer[]' is not assignable to type 'import("/project/workspace/node_modules/axios/index").AxiosRequestTransformer | import("/project/workspace/node_modules/axios/index").AxiosRequestTransformer[]'.
            Type 'AxiosRequestTransformer' is not assignable to type 'AxiosRequestTransformer | AxiosRequestTransformer[]'.
              Type 'import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).AxiosRequestTransformer' is not assignable to type 'import("/project/workspace/node_modules/axios/index").AxiosRequestTransformer'.
                The 'this' types of each signature are incompatible.
                  Type 'import("/project/workspace/node_modules/axios/index").InternalAxiosRequestConfig<any>' is not assignable to type 'import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).InternalAxiosRequestConfig<any>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
                    Types of property 'headers' are incompatible.
                      Type 'import("/project/workspace/node_modules/axios/index").AxiosRequestHeaders' is not assignable to type 'import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).AxiosRequestHeaders' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
                        Type 'AxiosRequestHeaders' is not assignable to type 'Partial<RawAxiosHeaders & { Accept: AxiosHeaderValue; "Content-Length": AxiosHeaderValue; "User-Agent": AxiosHeaderValue; "Content-Encoding": AxiosHeaderValue; Authorization: AxiosHeaderValue; } & { ...; }>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
                          Types of property 'Accept' are incompatible.
                            Type 'import("/project/workspace/node_modules/axios/index").AxiosHeaderValue' is not assignable to type 'import("/project/workspace/node_modules/axios/index", { with: { "resolution-mode": "import" } }).AxiosHeaderValue'.
                              Type 'AxiosHeaders' is not assignable to type 'AxiosHeaderValue'.
                                Type 'AxiosHeaders' is missing the following properties from type 'string[]': length, pop, push, join, and 33 more.

Minimal TypeScript Code Triggering Error

import axios from "axios";
import MockAdapter from "axios-mock-adapter";

export const axiosInstance = axios.create({});
new MockAdapter(axiosInstance);

TypeScript Config

{
    "compilerOptions": {
        "lib": [
            "esnext",
            "dom"
        ],
        "target": "ES2022",
        "module": "Node16",
        "moduleResolution": "Node16",
        "strict": true,
        "noFallthroughCasesInSwitch": true,
        "exactOptionalPropertyTypes": true,
        "forceConsistentCasingInFileNames": true,
        "esModuleInterop": true
    }
}
evelynhathaway commented 1 month ago

@remcohaszing I saw you were an author on axios's ESM types and you fixed #371. You seem knowledgeable about this area, and I hope you don't mind the ping. ❤️

remcohaszing commented 1 month ago

I think https://github.com/axios/axios/pull/6218 would resolve the issue.

yourinium commented 3 days ago

Any chance there is some sort of workaround to make this work until this is merged in?