kulshekhar / ts-jest

A Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.
https://kulshekhar.github.io/ts-jest
MIT License
6.95k stars 451 forks source link

Syntax Error with Optional Chaining in Typescript 3.7 #1283

Closed JoshRobertson closed 4 years ago

JoshRobertson commented 4 years ago

Issue :

I just upgraded a repo to use Typescript 3.7 to test out optional chaining. It compiles successfully (using webpack and @babel/preset-typescript). However when I run a test against the optional chaining syntax I get an error that Jest can't parse it.

Expected behavior:

Test should pass as normal

Error output:

 ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    filename.tsx:30
        const isStoreOwner = () => user?.isStoreOwner;
                                        ^

    SyntaxError: Unexpected token .

Jest.config.js:

  verbose: true,
  testURL: "http://localhost/",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
    "^.+\\.jsx?$": "babel-jest",
  },

  modulePathIgnorePatterns: ["<rootDir>/script/scaffold/", "<rootDir>/.*/__mocks__"],

  testRegex: "(/app/javascript/.*(test|spec))\\.(jsx?|tsx?)$",
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "d.ts"],

  snapshotSerializers: ["enzyme-to-json/serializer"],

  moduleNameMapper: {
    "^.+\\.(css|scss|sass|jpg|jpeg|png|gif)$": "<rootDir>/app/javascript/helpers/fileStub.ts",
    "\\.svg": "<rootDir>/__mocks__/svgr.tsx",
  },

  setupFilesAfterEnv: ["<rootDir>/app/javascript/testing/test-setup.ts"],

package.json:

    "@babel/core": "^7.7.0",
    "@babel/plugin-proposal-class-properties": "^7.7.0",
    "@babel/plugin-proposal-decorators": "^7.7.0",
    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4",
    "@babel/plugin-proposal-object-rest-spread": "^7.6.2",
    "@babel/plugin-proposal-optional-chaining": "^7.6.0",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/preset-env": "^7.7.1",
    "@babel/preset-react": "^7.7.0",
    "@babel/preset-typescript": "^7.7.0",
    "@types/enzyme": "^3.10.3",
    "@types/enzyme-adapter-react-16": "^1.0.5",
    "@types/jest": "^24.0.21",
    "enzyme": "^3.10.0",
    "enzyme-adapter-react-16": "^1.15.1",
    "enzyme-to-json": "^3.4.3",
    "fork-ts-checker-webpack-plugin": "^3.0.1",
    "jest": "^24.9.0",
    "ts-jest": "^24.1.0",
    "typescript": "^3.7.2",

I'm unsure which library is causing the error (ts-jest, jest, babel, etc) but this seemed like a good place to start.

ahnpnl commented 4 years ago

Hi, would you please provide a minimum reproduce repo ? I noticed in your above jest config seems to not have the complete config. It would be much easier to have a repo to check your issue.

ntucker commented 4 years ago

I have this same error and my tsconfig.json is:

{
  "compilerOptions": {
    "plugins": [{ "transform": "@zerollup/ts-transform-paths" }],
    /* Basic Options */
    "target": "esnext",                       /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    // "lib": [],                             /* Specify library files to be included in the compilation. */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    "jsx": "react",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    "declaration": true,                      /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    "outDir": "lib",                         /* Redirect output structure to the directory. */
    "rootDir": "./src",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "removeComments": true,                /* Do not emit comments to output. */
    // "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "baseUrl": "./src",                       /* Base directory to resolve non-absolute module names. */
    "paths": {
      "scheduler": ["types/scheduler.d.ts"],
      "scheduler/unstable_mock": ["types/scheduler/unstable_mock.d.ts"],
      "~/*": ["*"]
    },                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": ["./src"],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    "allowSyntheticDefaultImports": true,     /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    "emitDecoratorMetadata": true         /* Enables experimental support for emitting type metadata for decorators. */
  },
  "exclude": [
    "node_modules"
  ]
}
ahnpnl commented 4 years ago

hi @JoshRobertson , @ntucker , Please check my repo to see a working example with optional chaining. My guess is because you didn't inform ts-jest that you are using babel in your project which leads to the error.

ntucker commented 4 years ago

That made it work for me, though it didn't pick up my babel config from "src/.babelrc" like babel actually does. so I had to point to that file directly in the config instead of setting true.

ahnpnl commented 4 years ago

yes, you can either set to true which will tell ts-jest look for default location, or you can specify the path to babel config. You can find here the documentation

ahnpnl commented 4 years ago

Close as already provided a sample working repo

JoshRobertson commented 4 years ago

@ahnpnl does ts-jest support babel.config.js as opposed to .babelrc?

ahnpnl commented 4 years ago

Ts-jest supports all forms of babel config, including package.json, js and rc. In my example repo above, I used js config.

ntucker commented 4 years ago

"ts-jest look for default location" is this hardcoded? babel picks up my file fine, but ts-config didn't unless i specify the path.

ahnpnl commented 4 years ago

"ts-jest look for default location" is this hardcoded? babel picks up my file fine, but ts-config didn't unless i specify the path.

no default location isn't hardcoded. Default location means babel config on root level, this is default behavior of babel-jest itself. If you don't have babel config on root level then you will need to specificly tell ts-jest to look for it by specifying the path.

JoshRobertson commented 4 years ago

Just to follow up, the reason my babel config was failing was

 ["@babel/preset-env", { modules: false, useBuiltIns: "entry", corejs: 3 }],

the modules:false (which is needed for tree shaking in webpack) was causing syntax errors on import statements. Fortunately, this can be removed in Babel 7 and webpack will handle enabling module transformation automatically, allowing Jest to use the Babel config as well.

squalsoft commented 4 years ago

I changed tsconfig.json to have "target": "ES2019" and this fixed same issue.

dancon commented 4 years ago

I do not use babel, and I have the same issue.

It indeed works when I change the target to ES2015 - ES2020as @squalsoft mentioned above, but why ESNEXT doesn't work?

ahnpnl commented 4 years ago

https://stackoverflow.com/questions/58725711/how-to-get-optional-chaining-working-in-typescript

dancon commented 4 years ago

@ahnpnl got it, thanks a lot!

kalbert312 commented 4 years ago

Thanks @squalsoft. Fixed my issue. FWIW ES2020 also works but ESNEXT does not.

fedulovivan commented 4 years ago

Same issue is fixed for me with upgrading all **@babel/*** dependencies (to version 7.8.3 currently). But presumably only latest version of @babel/preset-typescript is needed. image

XaxisJosh commented 4 years ago

I needed more then @babel/preset-typescript. I upgraded it to the version @fedulovivan is using but still got compiler errors.

I was able to get optional chaining (and nullish coalescing) to compile by updating these two packages: @babel/preset-typescript@7.8.3 @babel/preset-env@7.8.4

PaliiIvan commented 4 years ago

Have same issue. I tried change target from ES15 - ESNEXT and nothing. Any suggestions? image

ahnpnl commented 4 years ago

@BloodShok , in your screenshot, it looks to me that you have errors in your codes and IDE identified it correctly.

PaliiIvan commented 4 years ago

@BloodShok , in your screenshot, it looks to me that you have errors in your codes and IDE identified it correctly.

Sorry I didn't specify, this is javascript after compiling TypeScript

ahnpnl commented 4 years ago

do you mean your screenshot above is the compiled result in javascript ?

PaliiIvan commented 4 years ago

yes

XaxisJosh commented 4 years ago

@BloodShok are you using TSC or babel to compile?

ahnpnl commented 4 years ago

the compiled result seems to not have the correct syntax at line return user ? .toObject() : ; . What is the original typescript codes for the above screenshot ?

PaliiIvan commented 4 years ago

Here TS: image

JS: image

I user TSC

I also noticed that when compiling TS also shows an error. Although the Ide doesn't show anything. Ts 3.8 version image

ahnpnl commented 4 years ago

so it's related to your tsconfig project. Indeed the compiled result isn't valid javascript. The above typescript code should result in

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
async function FindOne(query) {
    var _a;
    const user = await userAccountSchema.findOne(query);
    return (_a = user) === null || _a === void 0 ? void 0 : _a.toObject();
}
exports.FindOne = FindOne;

(this compiled result is with target: es2017, module: commonjs)

I'd suggest you to check your project config. It is not related to ts-jest

PaliiIvan commented 4 years ago

Ok, thank you.

jokester commented 4 years ago

It compiles successfully (webpack

One important difference with Jest is, the transformed code have to be executed (thus, supported) by current node version (this not true when bundling them for browser).

If tsconfig.json has target: esnext or similar, try specify a lower ES version like https://kulshekhar.github.io/ts-jest/user/config/tsConfig .

http417 commented 4 years ago

Optional chaining was added in ES2020, which isn't supported by node yet. So if your 'target' compile option is ES2020 or ESNext, then typescript compiler will see an optional chaining operator and leave it alone. If your target is ES2019 or further back then typescript will transpile the feature into something that ndoe will understand . like this:

const attributeScores = (_a = apiResponse === null || apiResponse === void 0 ? void 0 : apiResp...

svmszcck commented 3 years ago

Along with the solutions posted above, I had to move package.json configuration to separate jest.config.js file. Then it worked :) Maybe ts-jest can work with just separate jest config file?

ahnpnl commented 3 years ago

Along with the solutions posted above, I had to move package.json configuration to separate jest.config.js file. Then it worked :) Maybe ts-jest can work with just separate jest config file?

@svmszcck see https://kulshekhar.github.io/ts-jest/user/config/tsConfig

svmszcck commented 3 years ago

@ahnpnl But it didnt work when config is in package.json file. Now it is working. Weird -.- Anyway at least i could make it work :P

nandorojo commented 3 years ago

I solved this using ts-node by changing from Node v12 to v14.

nvm use 14

kroawen commented 3 years ago

I change the target to ES2019, it worked, but target: esnext not work. if change node version to 14.X, target: esnext also works.

jabjab42 commented 3 years ago

hi @JoshRobertson , @ntucker , Please check my repo to see a working example with optional chaining. My guess is because you didn't inform ts-jest that you are using babel in your project which leads to the error.

This worked for me thanks !!