Search code examples
typescripteslinttypescript-eslint

TypeScript ESLint incorrectly reports: "Function is defined but never used."


I have iwAddClassAndRemoveInSiblings function in lib\iw-browser.ts file:

"use strict";

/* Adds given CSS class to given element and remove this class in element's siblings.
   Equal to jQuery: $(element1).addClass(CSSClass).siblings().removeClass(CSSClass) */
function iwAddClassAndRemoveInSiblings(element: Element, CSSClass: string): void {
  for (const sibling of element.parentNode.children)
    sibling.classList.remove(CSSClass)
  element.classList.add(CSSClass)
}

I call this function in lib\iw-carousel\iw-carousel.ts file:

document.addEventListener('click', (event) => {
  const target = <HTMLElement>event.target
  if (target.matches('.iw-carousel__indicators li'))
    iwCarouselShowSlide(target.closest('.iw-carousel'), Number(target.dataset.slideTo))
})

/* Shows i-th slide of the given iw-carousel. */
const iwCarouselShowSlide = (carousel: HTMLElement, slideIndex: number) => {
  const slides = carousel.querySelectorAll('.iw-carousel__item')
  iwAddClassAndRemoveInSiblings(slides[slideIndex], 'active')
}

The compiled iw-browser.js and iw-carousel.js are referenced in iw-carousel.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  <script src="lib/iw-carousel/iw-carousel.js"></script>
  <script src="lib/iw-browser.js"></script>
</head>
<body>
   <!-- unimportant html content -->
</body>
</html>

Unfortunately, Typescript ESLint incorectly reports the iwAddClassAndRemoveInSiblings function as unused, both in Visual Studio Code, and if run it from the command line npx eslint . --ext .ts:

'iwAddClassAndRemoveInSiblings' is defined but never used. eslint(@typescript-eslint/no-unused-vars)

The error in Visual Studio Code

The HTML page is displayed properly - it runs iwAddClassAndRemoveInSiblings function without problems. Visual Studio Code also knows that iwAddClassAndRemoveInSiblings function is used. If I try to use a nonexistent function, VSC says: Cannot find name 'nonExsistingFunction'. So VSC checks if the function is defined. I have this problem only with ESLint.

Have I configured ESLint or Typescript incorrectly?

I have installed TypeScript and ESLint in the way described at: How to use ESLint TypeScript Airbnb configuration?

The Typescript and ESLint configuration files are below:

.eslintrc.js:

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser', // allows ESLint to understand TypeScript syntax
  parserOptions: {
    project: ['./tsconfig.json'],      // required for "type-aware linting"
  },
  plugins: [
    '@typescript-eslint',              // allows to use the rules within the codebase
  ],
  extends: [
    'airbnb-typescript/base',          //use Airbnb config
  ],
  rules: { }
};

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2016",
    "module": "commonjs",
    "sourceMap": true
  }
}

package.json:

{
  "name": "iw-components",
  "version": "0.1.0",
  "description": "...",
  "main": "index.js",
  "directories": {
    "lib": "lib"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/iwis/iw-components.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/iwis/iw-components/issues"
  },
  "homepage": "https://github.com/iwis/iw-components#readme",
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^2.34.0",
    "@typescript-eslint/parser": "^2.34.0",
    "eslint": "^6.8.0",
    "eslint-config-airbnb-typescript": "^7.2.1",
    "eslint-config-standard-with-typescript": "^16.0.0",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.1",
    "typescript": "~3.7.5"
  }
}

Solution

  • The problem is that ESLint doesn't know it's a global function. You have two options (at least):

    1. Don't use a globals — globals are historically a source of a lot of bugs. Use modules instead. Or,
    2. Tell ESLint that it's a global.

    There are two parts to #2: In the file where the function is defined, you need to tell ESLint that the function isn't "unused," and in the file where you use it, you need to tell ESLint about it.

    See the documentation for details, but basically:

    • To tell ESLint a function isn't unused, use a comment described here, e.g.:

      /* exported iwAddClassAndRemoveInSiblings */
      
    • To tell ESLint it exists (in the other file), use a comment described here, e.g.:

      /* global iwAddClassAndRemoveInSiblings */
      

    But I do strongly recommend using modules instead.