Search code examples
node.jstypescriptjestjsbabeljsts-jest

Jest failing to compile a function with generics


I am being able to run my Node app just fine, but when I try to test a class that has a function with Generics, I am getting an error.

The test code is as follows:

import { Request, Response } from 'express';
import { JsonWebTokenError } from 'jsonwebtoken';
import { action, api } from './base';

jest.mock('fs', () => ({
  readdirSync: (dirname: string) => ['test.ts'],
}));

describe('routes', () => {
  it('mounts routes with apiPath', async () => {
    @api('/sample')
    class SampleRoute {
      @action()
      action(req: Request, res: Response) {
        res.send({ pass: true });
      }
    }
  });
});

The class in question:

import { Request, Response } from 'express';
import Client from '../models/Client';

export function api<T>(path: string) {
  return function <T extends { new(...args: any[]): {}; }>(target: T) {
    return class extends target {
      static apiPath = path;
    };
  };
}

// (...)

Here's the error:

 FAIL  src/routes/index.spec.ts
  ● Test suite failed to run

    SyntaxError: /Users/fcoury/code/ds2-orders/packages/server/src/routes/base.ts: Unexpected token, expected "(" (4:19)

      2 | import Client from '../models/Client';
      3 |
    > 4 | export function api<T>(path: string) {
        |                    ^
      5 |   return function <T extends { new(...args: any[]): {}; }>(target: T) {
      6 |     return class extends target {
      7 |       static apiPath = path;

      at Parser._raise (../../node_modules/@babel/parser/src/parser/error.js:97:45)
      at Parser.raiseWithData (../../node_modules/@babel/parser/src/parser/error.js:92:17)
      at Parser.raise (../../node_modules/@babel/parser/src/parser/error.js:41:17)
      at Parser.unexpected (../../node_modules/@babel/parser/src/parser/util.js:140:16)
      at Parser.expect (../../node_modules/@babel/parser/src/parser/util.js:117:28)
      at Parser.parseFunctionParams (../../node_modules/@babel/parser/src/parser/statement.js:1119:10)
      at Parser.parseFunction (../../node_modules/@babel/parser/src/parser/statement.js:1087:10)
      at Parser.parseFunctionStatement (../../node_modules/@babel/parser/src/parser/statement.js:553:17)
      at Parser.parseStatementContent (../../node_modules/@babel/parser/src/parser/statement.js:195:21)
      at Parser.parseStatement (../../node_modules/@babel/parser/src/parser/statement.js:158:17)

My babel.config.js:

module.exports = {
  presets: [
    ['@babel/preset-env',
      {
        targets: {
          node: 'current'
        }
      }
    ]
  ],
  plugins: [
    [
      "@babel/plugin-proposal-decorators",
      {
        legacy: true,
      }
    ]
  ]
}

And tsconfig.json:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "experimentalDecorators": true,
  },
  "exclude": [
    "node_modules",
    "dist"
  ]
}

And finally my package.json:

{
  "name": "@ds-orders/server",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "@types/jsonwebtoken": "^8.5.0",
    "@types/moment": "^2.13.0",
    "babel-preset-es2020": "^1.0.2",
    "bcrypt": "^5.0.0",
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "knex": "^0.21.17",
    "lodash": "^4.17.20",
    "moment": "^2.29.1",
    "objection": "^2.2.14",
    "objection-visibility": "^1.1.0",
    "pg": "^8.5.1"
  },
  "scripts": {
    "start": "nodemon --watch 'src/' --exec 'ts-node src/server.ts' -e ts"
  },
  "devDependencies": {
    "@babel/core": "^7.12.13",
    "@babel/plugin-proposal-decorators": "^7.12.13",
    "@babel/preset-env": "^7.12.13",
    "@babel/preset-typescript": "^7.12.13",
    "@types/bcrypt": "^3.0.0",
    "@types/cors": "^2.8.9",
    "@types/express": "^4.17.11",
    "@types/jest": "^26.0.20",
    "@types/lodash": "^4.14.168",
    "babel-jest": "^26.6.3",
    "babel-preset-esnext": "^1.1.3",
    "babel-preset-typescript": "^7.0.0-alpha.19",
    "jest": "^26.6.3"
  }
}

Any idea how I can overcome this?


Solution

  • You can try adding @babel/preset-typescript

    babel.config.js
    //...
    plugins: [
        [
          "@babel/plugin-proposal-decorators",
          {
            legacy: true,
          }
        ],
        "@babel/preset-typescript"
      ]
    
    

    You can check an example repo I've created