Determine the generic types of callback in TypeScript based on the argument provided

There are numerous Stack Overflow questions that share a similar title, but it seems none of them address this particular inquiry.

I'm in the process of developing a wrapper for an express RequestHandler that can catch errors in asynchronous handlers and then call next with the error.

The concept is to be able to use code like the following:

function tryCatchNext<
  P = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery = ParsedQs,
  Locals extends Record<string, any> = Record<string, any>
>(
  callback: RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals>
): RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> {
  return async (req, res, next) => {
    try {
      await callback(req, res, next);
    } catch (err) {
      next(err);
    }
  };
}

app.get('/:foo', tryCatchNext(async (req, res, next) => {
  // `foo` is not a known parameter, but no error
  if (req.params.foo === 'bar') {
    throw new Error('That was a bar.');
  }

  // does not result in an error
  // @ts-expect-error
  console.log(req.params.bar);

  res.send('Not a bar');
}));

When I have just the handler, req has access to its params. However, within tryCatchNext(), req loses that access. I believe this is due to attempting to infer the return value from the callback provided instead of the generic types in its context.

Everything functions perfectly if I precede it with a dummy handler so that the ...handlers rest parameter of .get() is already inferred.

app
  .get('/:foo', (req, res, next) => next(), tryCatchNext(async (req, res, next) => {
    // ...
  }));

Is there a method to work around this issue without explicitly specifying each of the generic type parameters?

Answer №1

When using the RequestHandler in Express, you can simplify your code by utilizing the Parameters utility type to determine the type of the callback accepted as the second parameter of the app.get function. Here's how you can do it:

type CallbackType = Parameters<typeof app.get>[1];

function tryCatchNext( callback: CallbackType ): CallbackType {
  return async (req, res, next) => {
    try {
      await callback(req, res, next);
    } catch (err) {
      next(err);
    }
  };
}

If you explore the type definitions further (doc), you might find more accurate typings, but this approach should work effectively.

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

Using Firebase orderByChild to access a nested object in the database

In my current project, I am utilizing a real-time database with the following data structure: { "users": { "1234": { "name": "Joe", "externalId": "384738473847", }, ...

Using TypeScript to efficiently filter an Array by converting all values to lowercase

I have a custom array in TypeScript that needs to be filtered based on the city and job, with case-insensitivity as a requirement. array = [{ name: "Hardik", city: null, job: null }, { name: "John", city: "Ahmedabad", job: "IT" }, { name: "Margie", c ...

Unable to retrieve the third attribute of a Class using Angular2's toString method

Here is the code snippet I am working with: import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: ` <h1>Hello {{name}}</h1> <p><strong>Email:</strong> {{email}}< ...

How will the presence of @types/react impact the higher-level files in my project?

Here is the structure of my directories vue node_modules src react_app node_modules @types/react package.json ...other file package.json Why does the presence of "@types" in a subdirectory affect the top-level directory? I ...

What is the process for generating an object type that encompasses all the keys from an array type?

In my coding journey, I am exploring the creation of a versatile class that can define and handle CRUD operations for various resources. The ultimate goal is to have a single generic class instance that can be utilized to generate services, reducer slices, ...

What is the solution for combining multiple String Literal union types?

I'm dealing with two distinct types of string literals: type X = { type: "A1", value: number } | { type: "A2", value: string }; type Y = { type: "A1", test: (value: number) => void; } | { type: "A2", test: (valu ...

Encountering intellisense problems while declaring or utilizing TypeScript types/interfaces within Vue.js Single File Component (SFC) documents

For my Nuxt 3 project, I am developing a component and attempting to declare an interface for the component props to ensure strong typings. Here is an example: <script setup> interface Props { float: number; } const props = defineProps<Props> ...

What is the best way to make a class available in the global namespace within a TypeScript module?

The Issue at Hand Given an existing application with a mixture of modules and global scripts, the goal is to convert one class in a global script (gamma.ts) into a module. This entails adding `export default` before `class gamma {}` in gamma.ts. Additiona ...

The language in the React Native app remains unchanged despite utilizing i18next.changeLanguage

I am currently attempting to integrate the i18next library into my React Native app in order to facilitate language changes, but I have encountered difficulties with translation. image description here I have created an i18n.tsx file. import i18next from & ...

Arranging the output of a Typescript project

I'm currently advocating for Typescript to be implemented in our web application. Can anyone provide insight on the best method for structuring Javascript output files? Should they be excluded from source control? And when it comes to testing, is it p ...

Creating a new tab with the same html template binding in Angular 4

Could a button be created that, when clicked, opens a new browser tab featuring the same component and variables declared previously? <label>Please Enter Your Email Below</label> <input name="userEmail" type="text" class="form-control" re ...

A step-by-step guide on setting up a database connection with .env in typeorm

I encountered an issue while attempting to establish a connection with the database using ormconfig.js and configuring .env files. The error message I received was: Error: connect ECONNREFUSED 127.0.0.1:3000 at TCPConnectWrap.afterConnect [as oncomplete] ( ...

Angular 2 offers a powerful feature called ngFor that allows developers to

Is there a way to allow users to enter keywords in an input field to filter items within a list of menu items dynamically without using ngModel? I need this to be done without the use of buttons as well. Any suggestions for a workaround? <div class=" ...

Netlify failing to build CRA due to inability to locate local module for method?

I encountered an issue with deploying my site on Netlify. The problem arises when it fails to locate local modules. Below is the log: 12:54:43 AM: Build ready to start 12:54:45 AM: build-image version: 09c2cdcdf242cf2f57c9ee0fcad9d298fad9ad41 12:54:45 AM: ...

Mapping Interface Types in Typescript

I am currently exploring the correct method to map Interface record value types to the appropriate function type. function stringCompose(): string { return '' } function numberCompose(): number { return 0 } interface Demo { stringVal ...

The local types package cannot be built by react-scripts even though tsc has successfully completed the process

I have developed an application with a TypeScript frontend and backend. To streamline the process, I decided to create a shared types module that contains all the necessary types for both components in one centralized location. Rather than going through th ...

Using scrollIntoView() in combination with Angular Material's Mat-Menu-Item does not produce the desired result

I am facing an issue with angular material and scrollIntoView({ behavior: 'smooth', block: 'start' }). My goal is to click on a mat-menu-item, which is inside an item in a mat-table, and scroll to a specific HTML tag This is my target ...

"Enhancing User Experience with Hover States in Nested React Menus

I have created a nested menu in the following code. My goal is to dynamically add a selected class name to the Nav.Item element when hovering, and remove it only when another Nav.Item is hovered over. I was able to achieve this using the onMouseOver event. ...

A software piece producing a JSX element that generates a single function

Is it possible to create a JSX element or component that returns a single function as its child? For instance: interface ComponentChildrenProps { someProp: string; } const Component: React.FC<ComponentProps> = ({ children }): JSX.Element => { ...

Issue with MUI icon import: React, Typescript, and MUI type error - Overload does not match this call

Within my component, I am importing the following: import LogoutIcon from "@mui/icons-material/Logout"; import useLogout from "@/hooks/auth/useLogout"; const { trigger: logoutTrigger } = useLogout(); However, when utilizing this compo ...