Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding TypeScript and SystemJS WITHOUT Angular

I've been researching and doing simple "hello worlds" with TypeScript recently. There is something I think I can't wrap my head around and that is how to use System.js with TypeScript. Every single tutorial or demo out there on the internet is about Angular2 and I don't want to get involved with Angular 2 yet.

As an example, I have the following project structure:

RootFolder
| 
| _lib
| ...... ts (where .ts files are)
|
| components (where compiled .js files are)
| libraries
| ......... systemjs (where system.js is)
|
| index.html
| tsconfig.json

My tsconfig.json file is looking like:

{
  "compileOnSave": true,
  "compilerOptions": {
    "noImplicitAny": true,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "outDir": "./components"
  },
  "exclude": [
    "node_modules",
    "wwwroot"
  ],
  "include": [
    "./_lib/ts/**/*"
  ]
}

TypeScript compilation works as expected and there are no problems with that. I have created a simple class named "Alerter" contains the following code:

//alerter.ts
class Alerter {
    showMessage(): void {
        alert("Message displayed.");
    }
}

export default Alerter

And an app.ts (which is my "main" application file) with the following code:

//app.ts
import Alerter from "./alerter";

console.log("app.js included & executed");

function test() {
    console.log("test called");
    const alt = new Alerter();
    alt.showMessage();
};

And in my index.html I just want to import this app.js with System.js and simply want to call "test" function from the console. But it does not work. No matter what I did I simply can't access the function. I see the first console.log line being executed but when I try to call test() from chrome console it is undefined.

If I remove the "alerter" class dependency from my main.ts everything works. Because compiled app.js only contains console.log calls and the function definition.

Here is my System.js calls in index.html

System.config({
    packages: {
        "components": {
            defaultExtension: "js"
        }
    }
});

System.import("components/app");

I'm really desperate now and think I should simply go back to Jquery days. This is so simple yet can't make it work.

like image 380
Tequilalime Avatar asked Mar 30 '17 11:03

Tequilalime


1 Answers

I see what's going on here. This is related to both proper usage of TypeScript export keyword and SystemJS.

From your description you basically want to use SystemJS to import a JavaScript file similarly to using just the <script> tag and then use its global defined functions.

But this means you need to be aware of how TypeScript compiles your files. The documentation at https://www.typescriptlang.org/docs/handbook/modules.html says:

In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module.

This is what you're doing. The app.ts file has one import and the alerter.ts file has one export statement so these both are going to be compiled as modules. Then from your tsconfig.json I see you're using the system format (however, it doesn't matter here).

One major benefit of modules is that they don't leak any global objects outside the of their scope. So when you call System.import("components/app") the test() function exists only within that module. But you can export the function and call it after the module is loaded:

This means you need to export the function first:

// app.ts
export function test() {
  ...
};

Then System.import() returns a Promise that is resolved with the module exports object so we can call the test() method there.

System.import("components/app").then(function(m) {
  m.test();
});

This really works as you expect.

However, it looks like you wanted to define the test() function globaly. In such case you need to define the function on the window global object yourself:

// app.ts
function test() {
  ...
}
declare const window;
window.test = test;

Now you can use it whenever you want after importing the package:

System.import("components/app").then(function(m) {
  test();
});

SystemJS has multiple ways to manipulate with global objects but I don't think there's any easier way to use them when your imported package has dependencies that need to be resolved as well (otherwise have a look at this but it's not your use-case https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#exports).

like image 88
martin Avatar answered Nov 29 '22 18:11

martin