Definitelytyped: webpack-env contains "module" and "require" in global namespace

Created on 19 Sep 2016  路  10Comments  路  Source: DefinitelyTyped/DefinitelyTyped

As stated in issue #8018, node and webpack-env both pollute the global namespace with module and require. Would it be wrong to wrap webpack-env in a module? The API wouldn't be identical to the documentation, but we could then have both definitions live side-by-side.

I'm basically proposing the following

declare module "webpack-env" { // Wrapped with a module
    namespace __WebpackModuleApi {
       // No changes here
    }


    namespace e {
        // Add everything here that's currently declared globally
    }
    export = e;
}

Implementation would now look like this:

import {module, require as Require } from 'webpack-env';

Any objections?

Most helpful comment

I have found another way to solve the problem in webpack-env/index.d.ts:

interface NodeRequire extends __WebpackModuleApi.RequireFunction {
}

declare var require: NodeRequire;


interface NodeModule extends __WebpackModuleApi.Module {}

declare var module: NodeModule;

This solves the problem, and is more accurate as well, as it reflects what is really happening in JavaScript (webpack is modifying the require and module objects, from my limited understanding).

It means that you could use @types/node and @types/webpack-env together and get no compilation errors, or just @types/webpack-env and it will still work.

The only downside is that the variables will be represented with their Node types, but it should be easy to see that they derive from webpack types.

Happy to do a PR of this change if you are interested.

(There is one small change required to __WebpackModuleApi.RequireFunction interface to enable this, the PR will make it clear.)

All 10 comments

Both node.d.ts and webpack-env.d.ts define environment-specific globals. I wouldn't say they "pollute" the global namespace. The global namespace already has those variables defined in the respective environment (be it node.js or browser) and the definitions only make sure those are correctly typed in TypeScript as well. As lib.d.ts contains browser specific variables (e.g. location or window), webpack-env defines variables that are made available globally by webpack.

The proposal doesn't make much sense for me conceptually, nor technically. By design, webpack statically analyzes the input files and looks for calls to require (and not only). That's just the way it works. Though you could perhaps wrap some type definitions in a module, you still have to use the require runtime global directly. Then you would end up declare-ing it anyway in every single file:

import {WebpackRequire} from 'webpack-env';
declare var require: WebpackRequire;

Also, node.d.ts and webpack-env.d.ts are mutually exclusive because they represent different environments, even though the APIs for module and require look similar.

The problem is that with the new @types packages in TypeScript 2.0, I can't prevent node.d.ts from being installed, because loads of packages depend on @types/node. E.g. @types/bunyan depend on @types/node.

But if I then want to use @types/webpack-env, I can't because the require and module variables conflict in the global namespace. So using modules/namespacing here seems to make sense to allow sane usage of webpack-env?

@rikoe , namespacing is not possible. The only possibility is that node.d.ts can be somehow overridden or replaced with a trimmed version.

I have found another way to solve the problem in webpack-env/index.d.ts:

interface NodeRequire extends __WebpackModuleApi.RequireFunction {
}

declare var require: NodeRequire;


interface NodeModule extends __WebpackModuleApi.Module {}

declare var module: NodeModule;

This solves the problem, and is more accurate as well, as it reflects what is really happening in JavaScript (webpack is modifying the require and module objects, from my limited understanding).

It means that you could use @types/node and @types/webpack-env together and get no compilation errors, or just @types/webpack-env and it will still work.

The only downside is that the variables will be represented with their Node types, but it should be easy to see that they derive from webpack types.

Happy to do a PR of this change if you are interested.

(There is one small change required to __WebpackModuleApi.RequireFunction interface to enable this, the PR will make it clear.)

If I got it right, you are augmenting the NodeRequire interface from node.d.ts to extend from the webpack-env interface. Interesting approach. Although not 100% correct, as it adds properties that are not available with the webpack version of require, I think it's an acceptable compromise.

The interesting thing is that NodeRequire and NodeModule are global interfaces defined in node.d.ts, but I can redefine them in webpack-env.d.ts and so long as the members match when including both, the TypeScript compiler is happy with it.

I agree that it is not ideal, but given that with the @types model people are very likely to have @types/node in their package.json, I think it is an acceptable compromise as well. Certainly if you are using webpack-env on it's own, you will only see it's members.

I will raise a PR later today, happy for people to accept/decline. Until this change makes it to npm (if it does), I will continue using typings instead.

I found out that to prevent conflict between @types/node and @types/webpack-env, I can configure my tsconfig.json files so that they explicitly use only the types I want for different parts of my app.

For example, I am using webpack-env on an Angular2 project. I have e2e tests requires protractor and protractor has dependency on @types/node.

I have a tsconfig.json under my e2e/ directory and a tsconfig.json for my angular app under src/.

In src/tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "types": [
      "core-js",
      "jasmine",
      "qs",
      "webpack-env"
    ]
  }
}

In e2e/tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "types": [
      "es6-shim", "jasmine", "node", "selenium-webdriver"
    ]
  }
}

Just thought I'd share what worked for me 馃槃

@iamchucky solution doesn't work for me as i need to use node as well as webpack-env.

Has there been any further development on this? Is there any way to use both without having these conflicts?

As an example I treid to use ApolloClient through the import of:

import ApolloClient from 'apollo-client';

Which leads to the overwrite of webpack-env which makes it impossible to use.

When I do this: npm i -D @types/webpack-env and I add this to my app.ts file: import {WebpackRequire} from 'webpack-env'; I got this error: error TS2306: File 'MyProject/node_modules/@types/webpack-env/index.d.ts' is not a module. I use TypeScript 2.4.1.

Also, node.d.ts and webpack-env.d.ts are mutually exclusive because they represent different environments, even though the APIs for module and require look similar.

As @use-strict said.
The real problem is that the method of use is wrong.

I created a repository about this issue.
https://github.com/ZSkycat/how-to-use-types-webpack-env

How to use

Configuration Mode

Use two tsconfig.json files separately, and configure the type definitions to include.

webpack environment
{
    "compilerOptions": {
        "types": ["webpack-env"],
    }
}

node environment
{
    "compilerOptions": {
        "types": ["node"],
    }
}

Inline Mode

Edit tsconfig.json to exclude type definitions. (webpack-env and node)

{
    "compilerOptions": {
        "types": [],
    }
}

Use The Triple-Slash Directives to import type definitions.

webpack environment
/// <reference path="../node_modules/@types/node/index.d.ts" />

node environment
/// <reference path="../node_modules/@types/webpack-env/index.d.ts" />
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jbreckmckye picture jbreckmckye  路  3Comments

csharpner picture csharpner  路  3Comments

ArtemZag picture ArtemZag  路  3Comments

Loghorn picture Loghorn  路  3Comments

JWT
svipas picture svipas  路  3Comments