You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Jest+ts-jest时误加载JS文件引发语法错误的解决咨询

Jest + ts-jest 优先加载TS文件解决语法错误问题

问题场景

首次使用Jest搭配ts-jest执行测试时,运行jest命令触发语法错误:Jest错误加载了tsc编译生成的同目录JS文件,而非原TS文件,导致抛出SyntaxError: Cannot use import statement outside a module。

错误日志

$ jest
 FAIL  src/lib/Core/Storage/JsonStorage.test.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • 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/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /app/src/lib/Core/Storage/JsonStorage.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import fs from 'fs/promises'
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      1 | import fs from 'fs/promises'
    > 2 | import JsonStorage from './JsonStorage'
        | ^
      3 | import type IStorage from './IStorage'
      4 |
      5 | jest.mock('fs/promises')

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1495:14)
      at Object.<anonymous> (src/lib/Core/Storage/JsonStorage.test.ts:2:1)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.235 s
Ran all test suites.

相关代码与配置

测试代码(JsonStorage.test.ts)

import fs from 'fs/promises'
import JsonStorage from './JsonStorage'
import type IStorage from './IStorage'

jest.mock('fs/promises')

describe('JsonStorage', () => {
  let storage: IStorage<object>

  beforeEach(() => {
    storage = new JsonStorage('')

    jest.resetAllMocks()
  })

  test('Storage json object to file', async () => {
    fs.writeFile = jest.fn()

    const data = {
      one: 'one',
      two: 'two',
    }
    const pathname = './mokefile.json'
    const json = '{"one":"one","two":"two"}'

    await storage.save(pathname, data)

    expect(fs.writeFile).toHaveBeenCalled()
    expect(fs.writeFile).toHaveBeenCalledWith(pathname, json, 'utf8')
  })
})

被测业务代码(JsonStorage.ts)

import fs from 'fs/promises'
import type IStorage from './IStorage'

export default class JsonStorage<T = object> implements IStorage<T> {
  constructor(private _space: string) {}

  async save(pathname: string, data: T) {
    const json = JSON.stringify(data, null, this._space)

    await fs.writeFile(pathname, json, 'utf8')
  }
}

tsconfig.json

{
  "extends": "@tsconfig/node18/tsconfig.json",
  "compilerOptions": {
    "module": "es2022",
    "moduleResolution": "node",
    "baseUrl": ".",
    "paths": {
      "@App/*": ["./src/lib/*"],
      "@config/*": ["./src/config/*"]
    },
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "alwaysStrict": true,
    "exactOptionalPropertyTypes": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noPropertyAccessFromIndexSignature": false,
    "noUncheckedIndexedAccess": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "useUnknownInCatchVariables": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  },
  "watchOptions": {
    "watchFile": "useFsEvents",
    "watchDirectory": "useFsEvents",
    "fallbackPolling": "dynamicPriority",
    "synchronousWatchDirectory": true,
    "excludeDirectories": ["node_modules"]
  },
  "include": ["src", "bin", "index.ts"],
  "exclude": ["node_modules", "./**/*.test.ts"]
}

jest.config.ts

import { type JestConfigWithTsJest, pathsToModuleNameMapper } from 'ts-jest'
import { compilerOptions } from './tsconfig.json'

type ModuleNameMapper = Required<JestConfigWithTsJest>['moduleNameMapper']

const moduleNameMapper = pathsToModuleNameMapper(compilerOptions.paths) as ModuleNameMapper

const jestConfig: JestConfigWithTsJest = {
  testEnvironment: 'node',
  preset: 'ts-jest/presets/default-esm',
  transform: {
    '^.+\\.tsx?$': [
      'ts-jest',
      {
        useESM: true,
        isolatedModules: true,
        tsconfig: 'tsconfig.jest.json',
      },
    ],
  },
  transformIgnorePatterns: [
    '^.+\\.jsx?$',
  ],
  modulePaths: [compilerOptions.baseUrl],
  moduleNameMapper,
}

export default jestConfig

解决方案

1. 调整Jest文件扩展名优先级

在jest.config.ts中添加moduleFileExtensions配置,将ts放在最前面,让Jest优先查找TS文件:

const jestConfig: JestConfigWithTsJest = {
  // ... 现有配置
  moduleFileExtensions: ['ts', 'js', 'json', 'node'],
}

2. 分离TS编译产物目录

修改主tsconfig.json的compilerOptions.outDir,把编译后的JS文件输出到单独目录(如dist),彻底避免源码目录中存在JS文件:

{
  "compilerOptions": {
    // ... 现有配置
    "outDir": "./dist"
  }
}

3. 排除源码目录中的JS文件

如果不想调整编译输出目录,可在jest.config.ts中添加modulePathIgnorePatterns,忽略源码目录下的JS文件:

const jestConfig: JestConfigWithTsJest = {
  // ... 现有配置
  modulePathIgnorePatterns: ['<rootDir>/src/.+\\.js$'],
}

4. 优化jest的TS转换配置

确保tsconfig.jest.json的配置适配ts-jest的转换需求,比如将module设为CommonJS(或保持ESM但确保ts-jest能正确处理):

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "CommonJS",
    "isolatedModules": true
  },
  "include": ["src/**/*.test.ts", "src/**/*.ts"]
}

内容的提问来源于stack exchange,提问作者UKRman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:24:57