elbywan / wretch

A tiny wrapper built around fetch with an intuitive syntax. :candy:
MIT License
4.79k stars 96 forks source link

How to extract catcher functions with TypeScript #226

Closed jsardev closed 3 months ago

jsardev commented 6 months ago

I've been trying to extract some of my catcher functions to reuse them in multiple places but can't make TypeScript satisfied.

import wretch, { Wretch, WretchError, WretchErrorCallback } from 'wretch';

// Here I want `error` and `req` be properly typed
const renewCredentials: ClientErrorCallback = async (error, req) => {
  // Renew credentials
  const token = await wretch('/renewtoken').get().text();
  // storeToken(token);
  // Replay the original request with new credentials
  return req
    .auth(token)
    .get()
    .unauthorized((err: WretchError) => {
      throw err;
    })
    .json();
};

const client = wretch('/api/v1').resolve((chain) =>
  chain.unauthorized(renewCredentials).forbidden(renewCredentials)
);

type InferClientErrorCallback<Client> =
  Client extends Wretch<infer T, infer C, infer R>
    ? WretchErrorCallback<T, C, R>
    : never;

type ClientErrorCallback = InferClientErrorCallback<typeof client>

I tried to create an infer type to get the right WretchErrorCallback to be used in all of the catcher functions, but without luck. I always end up with this error:

CleanShot 2024-03-24 at 10 09 53

I think you'll get the idea from the example code above. I'd very appreciate any help 🙏

The code above in a live environment: https://stackblitz.com/edit/vitejs-vite-effanv?file=src%2Fmain.ts

elbywan commented 4 months ago

Hey @jsardev, sorry for the late answer.

The following code seems to work on my end:

import wretch, { Wretch, WretchError, WretchErrorCallback } from './dist';

async function renewCredentials<T, C, R extends undefined>(error: WretchError, req: T & Wretch<T, C, R>) {
  // Renew credentials
  const token = await wretch('/renewtoken').get().text();
  // storeToken(token);
  // Replay the original request with new credentials
  return req
    .auth(token)
    .get()
    .unauthorized(err => {
      throw err;
    })
    .json();
};

const client = wretch('/api/v1').resolve((chain) =>
  chain.unauthorized(renewCredentials).forbidden(renewCredentials)
);