Search code examples
javascriptnode.jstypescriptexpressprototype

TypeScript class inheritance behaving differently than its ES6 equivalent


I'm building an error handling middleware for a node.js with express and TypeScript project. Within it I have a AppError class that extends Error. It looks like this:

class AppError extends Error {
  public readonly internalCode: number | undefined;

  public readonly httpCode: number;

  public readonly message: string;

  constructor(httpCode: number, message: string, internalCode?: number) {
    super();
    this.internalCode = internalCode;
    this.message = message;
    this.httpCode = httpCode;
  }

  public generateReport(): GenericReport {
    return {
      code: this.internalCode,
      message: this.message,
    };
  }
}

I'm currently throwing new AppError() inside a /error route to catch it with the following middleware, errorHandler:

function errorHandler(err: Error, request: Request, response: Response, next: NextFunction,): Response {
  if (err instanceof AppError) {
    const report = err.generateReport();

    return response.status(err.httpCode).json(report);
  }

  return response.status(500).json(err);
}

Inside the middleware I'm trying to find AppError on err [[Prototype]] chain, but the verification always fails in the TypeScript code. However, after transpiling it with Babel and running the JS version with node, err instanceof AppError resolves to true. Why doesn't it in the TS code?

In the example above, Object.getPrototypeOf(err) returns me Error {}, and changing err type to AppError or any doesn't affect the result.

To my understanding, the newly constructed object (after new AppError() runs) should be [[Prototype]]-linked to AppError. And running curl http://localhost:3333/error with the TS server on, I get { "internalCode": 1, "message": "This is an error", "httpCode": 500 }, which means that err is indeed created by AppError().

There's a working example of the problem in this GitHub repository. Any clarification on this topic will be greatly appreciated.


Solution

  • I took a look at your code. A few comments:

    1. Switching to a later ecmascript output might fix this, as your output does not generate classes right now, because EcmaScript 5 didn't have them yet.
    2. It's a good idea to not use ts-node tools, especially when you're trying to debug something like. It can give funky result, and it's nice to just be able to look at the real javascript being executed.
    3. I took a look at your repo, and I believe that you should probably just use instanceof for what you are trying to do. I think for the purpose of your error middleware, instanceof makes a bit more sense.

    If you're interested, I also wrote an error handling middleware that does a similar thing:

    https://github.com/curveball/problem/blob/master/src/index.ts