5

I'm building an application using typescript, node and electron.

I'm using jquery in the application and I've installed the @types/jquery package to have intellisense hints.

Next I created a test using mocha and spectron. Spectron makes use of webdriverio and exposes its API through some properties. I need to use these properties, so I installed @types/webdriverio to have intellisense hints.

Now, whenever I run the tsc tool to compile the project I get the following errors:

node_modules/@types/jquery/index.d.ts(36,15): error TS2451: Cannot redeclare block-scoped variable '$'.
node_modules/@types/webdriverio/index.d.ts(1898,18): error TS2451: Cannot redeclare block-scoped variable '$'.
node_modules/@types/webdriverio/index.d.ts(1899,18): error TS2451: Cannot redeclare block-scoped variable '$'.

The issue is that both packages declare a global $ variable. You can verify it also in their npm pages under "Global values":

https://www.npmjs.com/package/@types/jquery

https://www.npmjs.com/package/@types/webdriverio

What I don't understand is why tsc is trying to compile them together since I'm not using jquery and webdriverio in the same .ts file?

Also, even if I comment out the test, so I'm not referencing webdriverio in any place, when I run tsc I get the same errors. Probably tsc is compiling all the sources in node_modules/@types together. In fact, if I remove the node_modules/@types/webdriverio folder and run tsc again, I get no error (of course, as long as I keep the test code commented).

This is my tsconfig.json which is in the root of the project:

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "sourceMap": false,
        "inlineSourceMap": true,
        "inlineSources": true,
        "declaration": false,
        "outDir": "dist"
    },
    "include": [
        "src/**/*"
    ]
}

All my source code is in the src directory. Tests are in src/test.

Is there any configuration I can make to keep webdriverio and jquery types separated at compile time? Also, I've seen some code examples written in js where they are used together: is this not feasible in typescript?

Mic
  • 372
  • 3
  • 11

1 Answers1

0

It's been a long time but I found the solution!

You need to define two separate tsconfig: one for the main project and one for the tests.

tsconfig.project.json

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "sourceMap": false,
        "inlineSourceMap": true,
        "inlineSources": true,
        "watch": false,
        "declaration": false,
        "outDir": "dist",
        "typeRoots": ["./typings"],
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "src/test/*"
    ]
}

tsconfig.test.json

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "sourceMap": false,
        "inlineSourceMap": true,
        "inlineSources": true,
        "watch": false,
        "declaration": false,
        "outDir": "dist",
        "typeRoots": ["./typings"],
    },
    "include": [
        "src/test/*"
    ]
}

Then, in the package.json file define two different scripts to run tsc against the two tsconfig you just defined:

{
...
  "scripts": {
    "test": "tsc -p tsconfig.test.json && mocha --exit dist/test",
    "tsc": "tsc -p tsconfig.project.json",
    ...
  },
...
}

The key point is the typeRoots config which overwrites the default behavior of the typescript compiler. The default behavior is to load every package defined below any @types folder (so every @types installed in the node_modules folder is included). By overwriting this configuration, the @types in the node_modules are included only when referenced. Alternatively, you can replace the typeRoots configuration with

"types": []

From https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

@types, typeRoots and types

By default all visible “@types” packages are included in your compilation. Packages in node_modules/@types of any enclosing folder are considered visible; specifically, that means packages within ./node_modules/@types/, ../node_modules/@types/, ../../node_modules/@types/, and so on.

If typeRoots is specified, only packages under typeRoots will be included.

...

Specify "types": [] to disable automatic inclusion of @types packages.

Keep in mind that automatic inclusion is only important if you’re using files with global declarations (as opposed to files declared as modules). If you use an import "foo" statement, for instance, TypeScript may still look through node_modules & node_modules/@types folders to find the foo package.

Another important point is to separate the config files. This way you can exclude the test files when compiling the main project and vice versa. This is important because the main project includes jquery, while the test files include webdriverio. Compiling them together you would get the same errors described in the question.

Mic
  • 372
  • 3
  • 11