Search code examples
typescriptwebpackts-loader

TypeScript cannot find class defined globaly


I have this file

routing.ts

class RouteConfig {
  // Some implementation
}

which I use like this

app.module.ts

angular.module("ApplicationsApp", [
    "ApplicationsApp.Services",
    "ApplicationsApp.Clients",
    "ApplicationsApp.Application"])
    .config(RouteConfig);

I then import both of the previous files using

index.ts

import "./scripts/routing.ts";
import "./scripts/app.module.ts"";

I'm using webpack and ts-loader and index.ts is one of the entry point. The build succeeds, but when I run it I get this error

app.module.ts:5 Uncaught ReferenceError: RouteConfig is not defined
    at Object.<anonymous> (app.module.ts:5)
    at __webpack_require__ (bootstrap 755a82fd7c11e301b6c1:676)
    at fn (bootstrap 755a82fd7c11e301b6c1:87)
    at Object.<anonymous> (index.ts:4)
    at __webpack_require__ (bootstrap 755a82fd7c11e301b6c1:676)
    at fn (bootstrap 755a82fd7c11e301b6c1:87)
    at Object.defineProperty.value (events.js:302)
    at __webpack_require__ (bootstrap 755a82fd7c11e301b6c1:676)
    at logLevel (bootstrap 755a82fd7c11e301b6c1:722)
    at main.min.js:726

The configuration I give to ts-loader looks like

{
  configFile: paths.resolveOwn('config/tsconfig.json'),
  compilerOptions: {
    target: 'ES5',
    module: 'commonjs',
    moduleResolution: 'node',
    sourceMap: true,
    strict: true,
    typeRoots: [
      paths.resolveOwn('node_modules/@types'),
      paths.resolveApp('node_modules/@types')
    ]
  }
}

Do you know what I am doing wrong ? I looked at almost all the tsconfig.json options, but cannot find one that resolves my problem


Solution

  • The issue

    In routing.ts you don't have any export neither import: for TypeScript, it is a script. But you use Webpack and you import it: for Webpack, routing.ts is a module. At compile time, the class RouteConfig is accessible globally and your program compiles. But, at run time, the class RouteConfig is not accessible globally.

    Solution 1: The old way, load routing.js as a script

    You can compile routing.ts in a separate file routing.js. Then, in the HTML code, the compiled file must be load from a separate tag <script src="scripts/routing.js">.

    In this solution, do not import the file routing.ts from a module bundled by Webpack. Just ensure in tsconfig.json it is accessible by the TypeScript compiler.

    Solution 2: The ugly way, manually declare RouteConfig as a global variable

    You could do:

    // routing.ts
    class RouteConfig {
      // Some implementation
    }
    window["RouteConfig"] = RouteConfig;
    

    Then, in index.ts you import routing.ts.

    // index.ts
    import "./scripts/routing.ts";
    

    As a result, Webpack ensures routing.ts is executed. And your global variable becomes accessible at run time.

    Notice: This solution is based on a misunderstanding. For Webpack and at run time, routing.ts is not a script but a module that declares a global variable. For TypeScript (at compile time) it is a script that ends with something wacky and unmanaged about window.

    Solution 3: The ES6 way, all modules, nothing global

    In the ES6 way, do not create any global variable. All the code is in modules. First, you have to export your class:

    // routing.ts
    export default class RouteConfig {
      // Some implementation
    }
    

    Then, you can import it:

    // index.ts
    import RouteConfig from "./scripts/routing.ts";
    

    A documentation on ES6 modules: ES6 in Depth: Modules.