Matching the Expected Type with Custom SWR Hook's Return Type

Currently, I am working on integrating swr into my project to create a custom block hook using generics. The goal is to have this hook creator accept mapParamsToKey and request methods as parameters and generate an SWR hook function. However, there seems to be an issue with the return type of my custom hook, SWRResponse, which is not matching the expected type. Below is a snippet of the code I have written for reference.

import useSWR from 'swr';

type Params = {
  id: number;
};
type BlogPost = {
  id: number;
  title: string;
};

const mapParamsToKey = (params: Params) => ['blog-post', params.id];
const request = (params: Params): Promise<BlogPost> => {
  const data = { id: params.id, title: `Blog Post ${params.id}` };
  return Promise.resolve(data);
};

const useBlogPost = (params: Params) =>
  useSWR(mapParamsToKey(params), () => request(params)); 
// (params: Params) => SWRResponse<BlogPost, any, any>


// swr hooks creator
function createBlockHook<P, T = any>(config: {
  mapParamsToKey: (params: P) => Array<string | number>;
  request: (params: P) => Promise<T>;
}) {
  return (params: P) =>
    useSWR(config.mapParamsToKey(params), () => config.request(params));
}

const useBlogPost2 = createBlockHook<Params>({
  mapParamsToKey,
  request,
}); 
// (params: Params) => SWRResponse<any, any, any>
// SWRResponse data type is any, but `BlogPost` is expected (infer from `request` param)

Here is how the hook should ideally be used:

import client from './my-api-client';
type CommentParams = { id: number; } 
const useComment = createBlockHook<CommentParams>({
  mapParamsToKey: (params) => ['comment', params.id], 
  request: client.getComment, // (params: CommentParams) => Promise<Comment>
}); 

function Comment(props: { id: number }) {
  const swr = useComment(props); 
  if (swr.data) {
    // swr.data with type hint `Comment`
    return (
      <div>{swr.data.content}</div>
    )
  }
  return <div>...</div>;
}

Answer №1

If you're looking to specify the input and output of useSWR with your API, we were able to achieve that using a code structure similar to this:

type ApiEndpoint = {
  'posts/{postId}/comments': {
    input: { postId: string },
    output: { comments: Comment[] }
  },
  // ...

}

export function useCustomApi<E extends keyof ApiEndpoint>(
  endpointKey: E | null | undefined | false,
  params?: Partial<ApiEndpoint[E]['input']>,
  options?: SWRConfiguration,
) {
  return useSWR<ApiEndpoint[E]['output'], HTTPError>(
    endpointKey && [endpointKey, params],
    fetcher,
    options,
  );
}

To define your API in ApiEndpoint and make use of it, simply call

useCustomApi('posts/{postId}/comments', {postId})
. Make sure to adjust your fetcher function accordingly for properly typed results.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue: The inject() function can only be executed within an injection context. Issue: An untruthy value was expected to be truth

I'm currently in the process of setting up a unit test for my app.component. I've imported all the necessary components, but I keep encountering an error that's puzzling me. I activated "preserveSymlinks": true in my angular.json file, but t ...

Issue regarding custom CSS implementation in Angular project

Being a French speaker, I apologize in advance for any mistakes I might make. I am also fairly new to Angular, so if you could provide detailed explanations in your responses, it would be greatly appreciated. Thank you. I am trying to import a custom CSS ...

Angular threw an error saying: "Template parse errors: is not a recognized element"

I am attempting to utilize babel standalone within a react application to transpile Angular TypeScript. The transpiling process seems to be successful, however, I encounter an error when trying to import a component and use its selector within the template ...

Maintaining scroll position in React Router v6

My website's homepage displays a user's feed. When a user clicks on one of the feed cards, they are taken to a different URL. However, when the user returns to the homepage, it automatically scrolls to the top. I am looking for a way to preserve ...

Jasmine is raising an error: "TypeError: Unable to access the property 'client' of an undefined object"

While running test cases for the EditFlag component in Angular, I encountered an error stating TypeError: Cannot read property 'client' of undefined. Additionally, I am looking to add a test case for a switch case function. Can someone assist me ...

The error message "Property 'then' is not available on type 'void' within Ionic 2" is displayed

When retrieving data from the Google API within the function of the details.ts file, I have set up a service as shown below. However, I am encountering a Typescript error stating Property 'then' does not exist on type 'void'. this.type ...

Dealing with React and Firebase Authentication Errors: How to Handle Errors for Existing Accounts with Different Credentials

According to the documentation at https://firebase.google.com/docs/auth/web/google-signin#expandable-1, when error.code === 'auth/account-exists-with-different-credential', signInWithPopup() should return an error.email. However, in my specific c ...

Toggle the visibility of a modal in code across various components in an Angular 4 project using Typescript

As I was working on my university App, I encountered an issue while attempting to open a Bootstrap modal in code from a different component. Opening a component in code from the same component posed no problems for me as I use JQuery and it functions perfe ...

When the button is clicked, (ngSubmit) will be triggered

In my Angular2 Form Component, I have implemented two buttons with different functionalities. Button Submit: This button submits all the values to the API. Button Add: This button adds an object to an array. Here are the two methods: onSubmit() { this. ...

Ways to merge values across multiple arrays

There is a method to retrieve all unique properties from an array, demonstrated by the following code: var people = [{ "name": "John", "age": 30 }, { "name": "Anna", "job": true }, { "name": "Peter", "age": 35 }]; var result = []; people. ...

Using TypeScript for Type Inference in Fetch Operations

I created a custom Fetch wrapper in Typescript to fetch data from my API. I am facing an issue where the retrieved data is of type "any" and I want to convert it to a specific type, like "user". Here is the link to my codesandbox for reference: https://co ...

How come I am unable to fetch classes and enums from a namespace?

When using Typescript with pg-promise, I am facing an issue where I can't import the classes and enums as I normally would. Typically, when working with a library, I import a type, use it, and everything functions properly. However, in the snippet bel ...

Using Jasmine to Jest: Mocking Nested function calls

I am currently working on testing my TypeScript functions with Jasmine: //AB.ts export async function A() { } export async function B() { A(); } My goal is to unit test function B by mocking out function A to see if it is called. Here is the code I h ...

The act of employing `Function.prototype.run` within an Angular TypeScript class is deemed as illegitimate

Is there a way to globally define a new function called run within my Angular component as shown below? Function.prototype.run = function (delay: number) { // some content; }; However, the compiler shows an error that the Property 'run' does n ...

Enhance the functionality of Immutable.js field by integrating a custom interface in Typescript

Imagine a scenario where the property name is field, essentially an immutable object. This means that methods like field.get('') and other immutable operations are available for use. Nevertheless, I have my own interface for this field which may ...

Exploring the integration of LeafLet into Next JS 13 for interactive mapping

I'm currently working on integrating a LeafLet map component into my Next JS 13.0.1 project, but I'm facing an issue with the rendering of the map component. Upon the initial loading of the map component, I encountered this error: ReferenceError ...

"Using the map function in Javascript to iterate through an array and then implementing

I am working on a script that involves an array of the alphabet along with two sets of values. The goal is to determine if a given value falls within the range specified by these two values and then print out the corresponding letter from the alphabet. H ...

Global Enum in Typescript that is Optimized for Inlining during Compilation

I'm facing a challenge with an enum that is widely used in my project. Having to import it into every file is becoming cumbersome. Is there a way to define the enum in the .d.ts file so that it automatically gets included when compiled to Javascript? ...

The 'style' property is not found within the 'EventTarget' type

Currently, I am utilizing Vue and TypeScript in an attempt to adjust the style of an element. let changeStyle = (event: MouseEvent) => { if (event.target) { event.target.style.opacity = 1; Although the code is functional, TypeScript consist ...

Enable Ace Editor's read-only mode

I'm having some difficulty getting my ace-editor to work in read-only mode. I'm working with Angular 9 and I've attempted the following approach: In my HTML file, I have the following code: <ace-editor mode="java" theme="m ...