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

Typescript types do not make sense #4176

Closed
euberdeveloper opened this issue Oct 11, 2021 · 18 comments
Closed

Typescript types do not make sense #4176

euberdeveloper opened this issue Oct 11, 2021 · 18 comments

Comments

@euberdeveloper
Copy link

Describe the bug

I updated from axios 0.21.1 to 0.22.0 but typescript started giving me strange errors

I then noticed that the types declarations changed and they do not make much sense.

I can see that the post signature is:

post<T = never, R = AxiosResponse<T>>(url: string, data?: T | undefined, config?: AxiosRequestConfig<T> | undefined): Promise<R>

I don't really get why the response data object should be, by default, equal to the object passed as body, there is really no sense for that as a default behaviour. It should be AxiosResponse instead.

For the axios function, the returned object is even a never, in axios(config: AxiosRequestConfig<any>): AxiosPromise<never>.

Of course I can just put the types specifications, but it makes the code longer and ugly when there would be many times no reason for that.

To Reproduce

To reproduce it, just try to make a post request

const response = await axios.post(
                CONFIG.API_UNITN.CLESIUS.SECURITY.AUTH_SERVICE_URL,
                data.toString()
            ); // the response has AxiosResponse<string> type

Expected behavior

There should not be types assumptions such as the one made

Environment

  • Axios Version 0.22.0
  • Adapter http
  • Browser any
  • Browser Version any
  • Node.js Version any
  • OS: Linux Ubuntu 20
  • Additional Library Versions none

Additional context/Screenshots

Add any other context about the problem here. If applicable, add screenshots to help explain.

@rastographics
Copy link

After upgrading from 0.21.1 to 0.22.0, my existing codebase is showing Typescript errors now. The return type from axios.get() or event just axios() is now returning a response with never as the type??

This code used to work without problems on 0.21.1:

const writeableStream = createWriteStream(path);
const response = await axios({method:'get',url:url, responseType: 'stream' });
response.data.pipe(writeableStream);
response.data.on('end', () => resolve(path));
response.data.on('error', (err) => reject(err));

Now with 0.22.0 I get this:

image

Property 'pipe' does not exist on type 'never'

Something got messed up with the typings for the response...
I have to downgrade to 0.21.1 for now. Wish I could help troubleshoot this but it's beyond my skills.

@xinghul
Copy link

xinghul commented Oct 11, 2021

I think the typings for axios.post is simply wrong, as @euberdeveloper pointed out, assuming response data is the same shape as post params makes no sense.

@majidsajadi
Copy link

this is actually my current issue. and I thought maybe I'm mistaken. and for the past two hours, I was searching the web for a possible fix.
Does anyone have any suggestions?

@rastographics
Copy link

this is actually my current issue. and I thought maybe I'm mistaken. and for the past two hours, I was searching the web for a possible fix. Does anyone have any suggestions?

downgrade to 0.21.1, works again.

@nayurin
Copy link

nayurin commented Oct 12, 2021

post<T = never, R = AxiosResponse<T>>(url: string, data?: T, config?: AxiosRequestConfig<T>): Promise<R>;

that means post() returns a promise which has a type of AxiosResponse<T>.
T means the type of request body.

maybe we can temporary solve this problem by using any...

await axios.post(url, data).then((resp: AxiosResponse<any>) => { do something... })

@Tobio89
Copy link

Tobio89 commented Oct 12, 2021

You certainly can solve this issue by adding any
Like so:

const { data }: any = await instance.get("/users/me");

Glad to see I'm not the only one with it! It'd be nice to see what the axios team think to this issue.

I've also had issues adding interceptors - also type issues:

(config: any) => {
    const token = useStore.getState().authToken;

    if (token) {
      config.headers.jwtAuthToken = token;
    } else {
      if (instance && instance.defaults.headers) {
        delete instance.defaults.headers.jwtAuthToken;
      }
    }
    return config;
  },

without (config:any) there, config no longer believes it should have a headers property :|

@yodosan
Copy link

yodosan commented Oct 12, 2021

If I'm not wrong, the types definition seems correct on master

post<T = unknown, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;

Hopefully, we need just to wait for the next release

@jasonsaayman
Copy link
Member

Yeah the next release should mend this, will probably release in the next hour or so

@doitandbedone
Copy link

This has been fixed in v.0.23.0 by #4116. Can be closed as duplicate.

@Gabb-c
Copy link

Gabb-c commented Oct 14, 2021

So, maybe I faced a similar problem:

public async getContestTypeByName(name: string): Promise<ContestType> {
    return new Promise<ContestType>((resolve, reject) => {
      this.api
        .get(`${Endpoints.ContestType}/${name}`)
        .then((response: AxiosResponse<ContestType>) => resolve(response.data)) // ts error in this line
        .catch((error: AxiosError<string>) => reject(error));
    });
  }

TypeScript Error log:

Argument of type '(response: AxiosResponse<ContestType>) => void' is not assignable to parameter of type '(value: AxiosResponse<unknown, any>) => void | PromiseLike<void>'.
  Types of parameters 'response' and 'value' are incompatible.
    Type 'AxiosResponse<unknown, any>' is not assignable to type 'AxiosResponse<ContestType, any>'.
      Type 'unknown' is not assignable to type 'ContestType'.ts(2345)

Fixed by typing the axios.get method itself:

public async getContestTypeByName(name: string): Promise<ContestType> {
    return new Promise<ContestType>((resolve, reject) => {
      this.api
        .get<ContestType>(`${Endpoints.ContestType}/${name}`)  //  <- here
        .then((response) => resolve(response.data))
        .catch((error: AxiosError<string>) => reject(error));
    });
  }

Don't know if it's the right way, but works fine.
Reference

@icyJoseph
Copy link

icyJoseph commented Oct 14, 2021

I dont know how to type interceptors anymore. I have the following:

type TokenResponse = {
  token?: string;
};

client.interceptors.response.use(async (response: AxiosResponse<TokenResponse>) => {
  // Some code
  return response.data;
});

But I always got the following error:

Argument of type '(response: AxiosResponse) => Promise' is not assignable to parameter of type '(value: AxiosResponse<unknown, any>) => unknown'.
Types of parameters 'response' and 'value' are incompatible.
Type 'AxiosResponse<unknown, any>' is not assignable to type 'AxiosResponse<TokenResponse, any>'.
Type 'unknown' is not assignable to type 'TokenResponse'.

Ah, this looks like a legit bug with how the AxiosInterceptorManager is typed:

export interface AxiosInterceptorManager<V> {
  use<T = V>(onFulfilled?: (value: V) => T | Promise<T>, onRejected?: (error: any) => any): number;
  eject(id: number): void;
}

export class Axios {
  constructor(config?: AxiosRequestConfig);
  defaults: AxiosDefaults;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
// ...rest of methods
}

There's no way to inject the type of response you want to intercept, it defaults to unknown and that's it.

This might be correct typing, though, since the response interceptors see all types of data coming through.

Regardless, you are correct, there's no way to type that signature, and forcing everyone to write type guards to narrow down from unknown to some type you trust is perhaps not ideal.

If I understand the axios docs correctly this signature for AxiosInterceptorManager should do the trick.

export interface AxiosInterceptorManager<V extends AxiosResponse | AxiosRequestConfig> {
  use<T, D = any>(
    onFulfilled?: (value: V extends AxiosResponse ? AxiosResponse<T, D> : AxiosRequestConfig<T>) => typeof value | Promise<typeof value>,
    onRejected?: (error: any) => any
  ): number;
  eject(id: number): void;
}

That being said one should still call the interceptor like so:

type ResponseTypeA = {
  token?: string;
};

type ResponseTypeB = {
  token?: number;
};

type ResponseTypeC = {
  token?: Array<number>;
};

this.client.interceptors.response.use<
  ResponseTypeA | ResponseTypeB | ResponseTypeC
>(async (response) => {
  // Some code
  return response;
});

Because, literally, all types of responses pass through your interceptor.

@bidorffOL
Copy link

I have another problem with the interceptor typings: they do not allow for a 3rd 'options' parameter, despite what the documentation says : https://github.com/axios/axios#interceptors.

@maxim-green
Copy link

this is actually my current issue. and I thought maybe I'm mistaken. and for the past two hours, I was searching the web for a possible fix. Does anyone have any suggestions?

downgrade to 0.21.1, works again.

Yesterday i was thinking i'm so stupid i can't figure it out. Thank you for recovering my self-esteem 😎

@jasonsaayman
Copy link
Member

This should be mended in the latest version

@wingedrhino
Copy link

This should be mended in the latest version

Is there a new release? I still see 0.23.0

@Gabb-c
Copy link

Gabb-c commented Oct 26, 2021

AxiosResponse reverted to any in v0.24.0

@wingedrhino
Copy link

AxiosResponse reverted to any in v0.24.0

Thanks! I pulled in v0.24.0 and my code is back to working as before in v0.21.4.

@jasonsaayman
Copy link
Member

Hi 👋

Please could you retry with the latest pre-release version and open a new issue should this error still be relevant?

Thanks

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

No branches or pull requests