I am writing a little game engine in typescript, and when I compile it to javascript, I get an error when running the javascript. It compiles without error too.
My main entry file (main.ts) starts with these two lines:
require('./core/Obj');
require('./core/Component');
It builds out fine but when I run it, the second require has some issues and gives this error:
Uncaught TypeError: Class extends value undefined is not a function or null
core/Obj.ts
namespace GameEngine {
export class Obj {
// Some functions/methods
}
}
core/Component.ts
namespace GameEngine {
export class Component extends Obj {
}
}
Then once it is compiled, it looks something like this:
(function (exports, require, module, __filename, __dirname, process, global) { (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[
function(require,module,exports){
var GameEngine;
(function (GameEngine) {
class Component extends GameEngine.Obj { // Error is here
}
GameEngine.Component = Component;
})(GameEngine || (GameEngine = {}));
},{}],
5:[function(require,module,exports){
var GameEngine;
(function (GameEngine) {
class Obj {
}
GameEngine.Obj = Obj;
})(GameEngine || (GameEngine = {}));
},{}]
});
Here is the gulp task that I am running:
gulp.task('compile-engine', function () {
return browserify()
.add('./GameEngine/main.ts')
.plugin(tsify, {})
.bundle()
.on('error', function (error) { throw error; })
.pipe(source('gameEngine.js'))
.pipe(buffer())
.pipe(gulp.dest('build/'));
});
Each module has its own GameEngine namespace - as modules don't pollute the global scope. (In the compiled bundle in your question, you can see that they are separate.) There is an answer here that explains namespaces and modules.
In using tsify, you're using (external) modules. Things can be made simpler if you do away with the namespacing. The TypeScript Handbook has this to say about using namespaces with modules:
A key feature of modules in TypeScript is that two different modules will never contribute names to the same scope. Because the consumer of a module decides what name to assign it, there's no need to proactively wrap up the exported symbols in a namespace.
You could change the exports and imports to something like this:
core/Obj.ts
export class Obj {
// Some functions/methods
}
core/Component.ts
import { Obj } from "./Obj";
export class Component extends Obj {
}
main.ts
import { Component } from "./core/Component";
// Do something with Component
Related
I have a legacy app in Node.js v10 (but upgrading is an option) which uses CommonJS modules (require).
The codebase is written in JS.
Now, I want to refactor part of the project and write it in Typescript using TS modules (import/export) and classes.
What is the best way to interface those newly generated JS files (compiled from TS) from my legacy JS code?
Say I have a class in TS:
export default class DemoClass {
public static sayHello() {
console.log("Hello from demo class...");
}
}
which gets compiled to:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var DemoClass = /** #class */ (function () {
function DemoClass() {
}
DemoClass.sayHello = function () {
console.log("Hello from demo class...");
};
return DemoClass;
}());
exports.default = DemoClass;
//# sourceMappingURL=DemoClass.js.map
I have set "module": "commonjs" in my tsconfig.json.
Now, how do I require that DemoClass.js from my legacy app.js and e.g. invoke sayHello()?
The following code seems to work, but this "default" stuff does not seem right:
var DemoClass = require('./lib/DemoClass');
DemoClass.default.sayHello();
Instead of using export default, use export =.
export = class DemoClass {
public static sayHello() {
console.log("Hello from demo class...");
}
}
Playground link
I use Webpack as a package manager and ES6. Looks like an imported module is not correctly evaluated with eval.
How to eval a string using the imported Module ?
Code:
In a global scope:
<script type="text/javascript">
var tableDefinition = '{ value: "myvalue", myfunction: (v) => { return DateFormat.format(v) }}';
</script>
In my module:
import DateFormat from "~modules/DateFormat"; // a module helper written by me
class GeneriClass {
constructor() {
DateFormat.init();
let columns = eval(tableDefinition); // this code retrieves 'DateFormat' not defined
}
}
The error on eval is: "'DateFormat' not defined!"
In app.module.js file, import JS file from external lib
import 'assets/scripts/admin';
with global function:
function anonymousAdmin() {
"use strict";
implementation();
}
Now in app.component.js file have controller with function call:
export const AppComponent = {
controller: class AppComponent {
$onInit() {
/* global anonymousAdmin */
anonymousAdmin();
}
}
};
Run Webpack/Babel to save all files (ES6) into one file (ES5). Unfortunately i have error in console:
ReferenceError: anonymousAdmin is not defined
Someone knows how I can call this function in the controller?
anonymousAdmin is not global function. The file is imported as ES module, and ES modules force strict mode that prevents them from leaking variables to global scope.
It should be explicitly defined as a global:
function anonymousAdmin() {...}
window.anonymousAdmin = anonymousAdmin;
If the file belongs to third-party module that cannot be directly edited, and considering that Webpack is being used, the latter can be configured to expose a variable from file ES module export with exports loader, something like:
...
module: {
rules: [
{
test: require.resolve('assets/scripts/admin'),
use: 'exports-loader?anonymousAdmin'
}
]
},
...
And the function is being imported like:
import { anonymousAdmin } from 'assets/scripts/admin';
Try this one. You need to have alias for the script.
import * as admin from 'assets/scripts/admin';
export const AppComponent = {
controller: class AppComponent {
$onInit() {
/* global anonymousAdmin */
admin.anonymousAdmin();
}
}
};
I want to break up my file into several modules. One module has a constructor. I am able to import my module into another file, but I don't know how call my constructor in the new file.
namespace CreditReports{
export class CreditReportVM {
//some code
constructor(targetElement: HTMLElement) {
ko.applyBindings(this, targetElement);
this.init();
}
public init = () => {
//some code
}
}
}
You just need to export the namespace too.
export namespace CreditReports {
//...
}
Then when you want to call the constructor:
import { CreditReports } from "./my-module";
//...
new CreditReports.CreditReportVM(myElement);
You should replace "./my-module" with the file name (an path too) where your typescript module is in.
I am trying to use a third party Node module in an angular 2+ project.
installed with npm install --save ModuleName
The function in question is in a file named Foo.js and looks like this:
var foo = function(param1, param2) {
...
this.init();
}
foo.protoype = {
constructor: foo,
init: function(){
...
},
...
}
module.exports = foo;
index.js for the node module looks like:
var ModuleName = require("./src/ModuleName");
ModuleName.foo = require("./src/Foo");
module.exports = ModuleName;
I am trying to use the module in a Directive:
import { Directive, OnInit } from '#angular/core';
import { ModuleName } from "ModuleName"
#Directive({
selector: '[customDirective]'
})
export class CustomDirective implements OnInit {
constructor() {
...
}
ngOnInit() {
let poorlyNamedVariable = ModuleName.foo(param1, param2);
}
}
When foo is called it produces ERROR TypeError: this.init is not a function
console.log(this) in foo shows an instance of ModuleName which, in turn, has an instance of foo, which has a prototype where init is defined.
I suspect the problem stems from some sort of scoping issue, but am still too new to both Angular and Node to untangle the mess.
Use import * as ModuleName from "ModuleName" for CommonJS modules.