Lou
Lou

Reputation: 4474

Getting plain JavaScript files to work with the TypeScript module system

I am using Visual Studio to create a ASP.NET Core 2.2 web app using TypeScript, and I have included the two.js library along with the two.d.js (TypeScript declaration file) from the npm package I've found online.

The problem is that I don't understand how to get modules working (perhaps it's not related to two.js but using plain JavaScript with modules in general).

two.js file uses JSDoc, but doesn't have any exports as far as I can see.

So my project has these files (two.d.js, two.js and site.ts):

+ js
  - two.d.js
  - two.js
  - site.ts
  - something.ts

And site.js file is importing two.js and something.ts at the beginning:

// this uses the 'two.d.js' file and I get intellisense
import { Two } from "./two";

// this is some .ts file
import { Something } from "./something";

// create the Two.js instance
let placeholder = document.getElementById('placeholder');

// this line is what I want to get running
let twojs = new Two({ width: 1900, height: 885 }).appendTo(placeholder);

However, when site.ts gets compiled, any modules option I try fail in the browser:

  1. "None" (i.e. no module system): Fails with exports is not defined in the first line of the compiled site.js file:

     Object.defineProperty(exports, "__esModule", { value: true });
    
  2. ES2015 native module system: Fails with "404 not found" for ./something and ./two. TypeScript compiler doesn't append the .js extension to the native browser import statement, so these files cannot be loaded. However if I add the extension in TypeScript then I get a compile error.

  3. AMD/RequireJS: I get the "Two is not a constructor" exception. Code gets compiled to this and I can see all .js files loaded correctly, however I guess the problem is that two.js doesn't export anything natively:

    define(["require", "exports", "./something", "./two"], function (require, exports, something, two_1) {
    
        "use strict";
        Object.defineProperty(exports, "__esModule", { value: true });
        let placeholder = document.getElementById('placeholder');
    
        // Two is not a constructor
        let twojs = new two_1.Two({ width: 1900, height: 885 }).appendTo(placeholder);
    
    });
    

Ideally, I would like to have a single bundled .js file per each page to reduce module loader server roundtrips, but I am open to other options.

Upvotes: 4

Views: 1203

Answers (1)

hackape
hackape

Reputation: 19947

The two.js is not exporting anything, it's exposing Two as a global variable, thus the two.d.ts is bluntly WRONG at type-annotating the module (in fact two.js doesn't qualify as a "module"). You need to correct it first.

  1. You need to replace all top-level export keyword with declare keyword in two.d.ts. That'll tell TS two.js is NOT a module and instead it exposes some global variables.

  2. site.ts can now directly refer to Two the global variable, no more import needed. As a good practice, it's advised to place a triple slash directive at the top of this file to show the dependency relationship. This will also put the content of two.js before site.js when bundled (see next step).

/// <reference path="./two.js" />

let twojs = new Two({ width: 1900, height: 885 }).appendTo(placeholder);
  1. config tsconfig.json as following. Run tsc in command line, it will compile and bundle all your .js and .ts file into one single file. You might need to config "include" (or "files") field to cover all related resource, (e.g. two.js, two.d.ts) according to your project's file structure. Docs
{
    "include": [...],
    "compilerOptions": {
        "module": "amd",
        "outFile": "bundle.js",
        "allowJs": true  // to include `two.js` into the bundle
    }
}
  1. include your scripts in HTML using RequireJS.
<script data-main="scripts/bundle" src="scripts/require.js"></script>

Upvotes: 2

Related Questions