Search code examples
node.jstypescripteslinttypescript-eslint

Make "import/extensions" require the .js extension in a Node.js TypeScript project


First of all, some facts:

  • Node.js requires that all local imports include the imported module's extension (e.g. import hello from './hello.js', not import hello from './hello').
  • TypeScript will compile imports with or without the .js extension, which means a missing .js extension is a runtime error.
  • TypeScript doesn't transform imports to add the .js extension or convert .ts to .js.

In my Node.js project, I want to make missing a missing .js extension be a build-time error using the import/extensions ESLint rule. However, when I enable this rule using the following configuration:

{
  "root": true,
  "env": {
    "node": true
  },
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint"
  ],
  "extends": [
    "eslint:recommended",
    "plugin:import/recommended",
    "plugin:import/typescript",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "settings": {
    "import/resolver": {
      "typescript": {},
      "node": {
        "extensions": [".js"]
      }
    }
  },
  "rules": {
    "import/extensions": ["error", "ignorePackages"]
  }
}

running eslint gives me the following error:

/sandbox/src/index.ts
  1:19  error  Missing file extension "ts" for "./hello.js"  import/extensions

Source files:

// index.ts
import hello from "./hello.js";

hello();
// hello.ts
export default function hello() {
  console.log("Hello");
}

CodeSandbox link: https://codesandbox.io/s/elated-germain-13glp7


Solution

  • I fixed this with the following config:

    {
      "root": true,
      "env": {
        "node": true
      },
      "extends": [
        "eslint:recommended",
        "plugin:import/recommended",
        "plugin:import/typescript",
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended"
      ],
      "rules": {
        "import/extensions": ["error", "ignorePackages"],
        "import/no-unresolved": "off"
      }
    }
    

    The main thing is to disable the "import/no-unresolved" rule and remove "settings"."import/resolver"."node". ("import/no-unresolved" is redundant as unresolved imports are resolved at the compilation stage.) Other items removed here were already being added as a result of extending the @typescript-eslint plugins.