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

ErrorRequestHandler type not detected in VSCode #5203

Open
staplespeter opened this issue May 30, 2023 · 3 comments
Open

ErrorRequestHandler type not detected in VSCode #5203

staplespeter opened this issue May 30, 2023 · 3 comments
Labels

Comments

@staplespeter
Copy link

In Typescript, with "no implicit any" setting, I am unable to use the custom error handling OOTB. This appears to be an issue with the types. The following produces errors in VSCode and tsc.

import express, { NextFunction } from 'express';

const expressApp = express();
expressApp.use((req, res, next) => {});
expressApp.use((err, req, res, next) => {});  //<--------problem
expressApp.use((err: any, req: any, res: any, next: NextFunction) => {});

There is a repo here: https://github.com/staplespeter/expressjs-bug-types-ErrorRequestHandler

In the code inspector in VSCode the RequestHandler type is displayed for the 3-arg version and RequestHandlerParams for the 4-param version of the possible arguments to use().

I have had a look at, and played around with the typedefs to no avail.
Please let me know if i have missed something or if the types are not correctly defined.
Thanks!

@krzysdz
Copy link
Contributor

krzysdz commented May 31, 2023

@types/express package is provided by the DefinitelyTyped project. The problem with error handling in Express is known and looks like the only way to fix it is by manually specifying the error handler's type:

import express, { ErrorRequestHandler, NextFunction } from 'express';
//                ^^^^^^^^^^^^^^^^^^^

// ...

// (1) wrap the arrow function in () and add as ErrorRequestHandler
expressApp.use(((err, req, res, next) => {}) as ErrorRequestHandler);

// (2) function as a typed variable, IMO the best option
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {}
expressApp.use(errorHandler);

// (3) anonymous function instead of arrow with as ErrorRequestHandler doesn't require wrapping in ()
expressApp.use(function (err, req, res, next) {} as ErrorRequestHandler);

Related issues:

@VitorhugoBatista
Copy link

VitorhugoBatista commented Jun 13, 2023

You're correct that the built-in types for Express middleware functions do not correctly support error-handling middleware. This is because Express's types were written before TypeScript had proper variadic tuple type support.

To fix this, you'll need to write your own type definitions for Express's error middleware. Here's what that would look like:

import { Request, Response, NextFunction } from 'express';

type ErrorRequestHandler = (
err: any,
req: Request,
res: Response,
next: NextFunction
) => void;

expressApp.use((err: any, req: Request, res: Response, next: NextFunction) => {
// Type checks pass
});

By defining your own ErrorRequestHandler type, TypeScript now understands that the 4th argument is the NextFunction, and the type checks will pass.

The core issue here is that Express's types don't properly model variadic arguments or overloads, so for some of Express's more dynamic features, you'll need to supplement the types with your own definitions. Defining an ErrorRequestHandler type is a great example of improving Express's type definitions.

@krzysdz
Copy link
Contributor

krzysdz commented Jun 13, 2023

@VitorhugoBatista there already is an ErrorRequestHandler type in @types/express:

  1. declaration in @types/express extending the declaration from @types/express-serve-static-core
  2. declaration in @types/express-serve-static-core

this type is a better version of the one that you suggested and can be imported directly as shown in my previous response. The type basically is:

import * as core from 'express-serve-static-core';

interface ErrorRequestHandler<
    P = core.ParamsDictionary,
    ResBody = any,
    ReqBody = any,
    ReqQuery = core.Query,
    Locals extends Record<string, any> = Record<string, any>
> = (
    err: any,
    req: Request<P, ResBody, ReqBody, ReqQuery, Locals>,
    res: Response<ResBody, Locals>,
    next: NextFunction,
) => void;

Lack of variadic tuple support (which is supported since TS 4.0) is not a problem - there are only static overloads and variadic are unnecessary. The problem with overload resolution was reported as a TypeScript bug a long time ago, but the issue was closed as a design limitation: microsoft/TypeScript#31867.

You can check a simple version of this TS problem here.

Adding types to the middleware function parameters can resolve the error, but it is more work than just declaring the type of the error handler like I've shown previously.

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

No branches or pull requests

4 participants