I'm using TypeScript + AMD (RequireJs) and I'm facing a problem regarding circular dependency. tsc deals with circular dependency pretty smooth but when it's run time, it took me hours to figure out that I'm facing a circular dependency problem.
Here's my scenario:
Singleton.ts
import Window = require("./Window");
class Singleton
{
private w : Window;
private counter : number = 0;
constructor() {
w = new Window();
}
Increment() : number {
return ++this.counter;
}
}
export = Singleton;
Window.ts
import Singleton = require("./Singleton");
declare var global_instance : Singleton;
class Window
{
private my_number : number = 0;
constructor() {
this.my_number = global_instance.Increment();
}
}
export = Window;
As simple as it seems, it leads to a circular dependency and thus I'm unable to implement it in a browser. But as I said tsc deals with it perfectly without any problem. As I searched the Internet I found a couple of proposed solutions:
This one suggests adding a typeof before the type to prevent compiler from actually putting the type's file in require list. But it results in a dozen of compiler errors so it's out.
This one sounds promising but it's a RequireJs solution and I'm not sure how to implement it in TypeScript!
Does anyone have any solution how to deal with circular dependency while benefiting from type checking of tsc compiler?
Because these files are absolutely tied together, put them in a single file. Not only does it solve your circular reference, but it also means one less HTTP request (which you are guaranteed to need).
Once they are in the same file, you may decide to merge the concepts further, or split out that part that they both depend on to remove the circular dependency.
if you are using the requirejs.d.ts
amend it with this
declare var exports: any;
and then your class you explicitly call exports.
import Window = require("./Window");
class Singleton
{
private w : Window.Window;
private counter : number = 0;
constructor() {
w = new Window.Window();
}
Increment() : number {
return ++this.counter;
}
}
exports.Singleton = Singleton;
export = Singleton;
Window.ts
import Singleton = require("./Singleton");
declare var global_instance : Singleton.Singleton;
class Window
{
private my_number : number = 0;
constructor() {
this.my_number = global_instance.Increment();
}
}
exports.Window = Window;
export = Window;
I wish there was a way, such as in node to just set exports directly but that does not seem to work, at least for me.
Related
I am trying to use a more object oriented approach with node.js "embedding" functions ( if that is the right word ) so that I can use functions and objects as if they are in the objects context. It might be easier to show in code.
I realise you can assign individual functions in the constructor - and this would work.. but I am not sure how to assign a whole module with functions to all the functions can access values in the objects context.
So , my question is : How can I assign a module to a class so that all the functions within the module can access everything within the objects context.
app.js
const myFunctions = require('./functions');
class myClass{
constructor() {
this.myFunctions = myFunctions ;
}
}
var mc = new myClass();
mc.myObject = { aaa: 'test'}
mc.myFunctions.outputValue(); // << should output the previous value set.
functions.js
function outputValue(){
console.log(this.myObject)
}
module.exports = {
outputValue
}
You could do it in two ways:
1 - Bound your class instance this to each one of the external functions:
class myClass {
constructor() {
this.myFunctions = {
outputValue: myFunctions.outputValue.bind(this),
};
}
}
2 - Define a method in your class to call the external functions, like:
class myClass {
constructor() {
}
callFunction(fnName) {
const fn = myFunctions[fnName];
if (fn) fn.apply(this);
}
}
Said that I will recommend avoiding using classes and this at all (at least it's completely necessary) and instead use pure functions, functions that only receive parameters does some processing and return some value.
The best way to do this which also follows the injection pattern,
const myClass = new myClass(myFunctions);
myClass.outputValue.bind(myClass);
Here it binds and inject all the class objects so it is accessible to other methods in different class .
Note : Look at "bind" usage.
I have a ready() function which contain an es6 class.
I would like to instantiate this class outside the function, however I didn't succeed in making this working.
I don't want to use return function because the function can have more than one class.
Here is an example of what i want to do :
function ready(api) {
class A extends api.C {
contructor() {
this.foo = "bar";
}
}
class B extends A {
contructor() {
this.bar = "foo";
}
}
}
let classA = ready.A; // Here is the problem : I would like to access to the class prototype
The reason is my application have some plugins using remi npm package, and I want to export classes shared between all plugins.
I don't know if it's possible, anybody have an idea ?
[EDIT] :
First thanks all for your hep, In fact I need to be more accurate in my issue.
That's true, I can return back both classes through a classic return. But let me explain what I want to do exactly :
I have a core application which have some plugins. Plugins can be internally integrated to core, or through a classic NPM install. As explained above I use remi plugin loader.
The desired rules are the following : Each plugin get a core API access through a callback. Each plugin can expose classes to be used in other plugins. Each plugins can have inheritance for those exposed (ie exported) plugins.
Here is how I describe a plugin :
/**
* This class is a sample plugin
* #class
*/
class Sample {
constructor(api) {
this.api = api;
}
/**
* Shows a test log
*/
test() {
console.log("======> I'm working !");
}
}
function loaded(api) {
let s = new Sample(api);
}
module.exports = (api, options) => {
};
module.exports.attributes = {
loadedCallback: loaded,
name: "sample-plugin",
version: "0.0.0",
category: "misc",
description: "I'm a sample plugin",
classes: [Sample]
};
The core plugin manager will load the first time all plugins, and will register into an array all classes protoypes exported into the classes attributes property.
Then the loaded(api) function is called and classes can be instantiated through api.classes object.
In another plugin, I can do this :
function loaded(api) {
let sample = new api.classes.Sample(api);
}
And I can instantiate a class described in another plugin.
That's the idea.
However I would like to do a more complex API usage with the multiple inheritance, for example :
Plugin A exposes class A
Plugin B exposes class B extends A
Plugin C exposes class C extends B
To be more clear, I would like to export globally some classes, to get a shared access between NPM modules
You can return class A from ready function, so after calling ready you can access A class
function ready(api) {
class A extends api.C {
contructor() {
this.foo = "bar";
}
}
class B extends A {
contructor() {
this.bar = "foo";
}
}
return {A : A};
}
let classA = ready(api).A;
I am using ES6 syntax style to make the inheritance class in node.js, there is two classes, in which the base class is to build a mqtt client, and the inherited class is to extend the base class.
However, the problem is the inherited class can not use the variable defined in the base class.
For example, in my base class, there is a public variable called : this.mqtt_client, when I tried to use this variable in the inherited class, it always gives a undefined issue
My base class is as followed
var mqtt = require('mqtt'),
EventEmitter = require('events').EventEmitter;
class MQTTBaseClass extends EventEmitter{
constructor( option) {
super();
this.mqtt_client = null;
this.uri = option.uri;
this.mqttOptions.clientId = option.clientId;
}
init(uri, mqttOptions){
this.mqtt_client = mqtt.connect( uri , mqttOptions );
this.mqtt_client.on('connect', onConnectionSuccess);
this.mqtt_client.on('error',onConnectionError);
this.mqtt_client.on('message',onMessageReceived);
............
}
}
class MQTTClass2 extends MQTTBaseClass{
constructor(option) {
super(option);
var self = this;
var interval = setInterval(
function() {
self.mqttClient.publish('dddd' , 'ddd' , { retain: false },function(err){
})
}, 5000);
}
..............
}
I've made the assumption that the base class init function is being called that defines this.mqtt_client.
The issue appears to be a misspelling, you are using self.mqttClient where you should be using self.mqtt_client.
As a side note you should attempt to use a common variable naming scheme to avoid issues like this in the future: most Javascript code is written using camel case, but there is no rule against using underscores. The important thing is to be consistent.
The error is completely correct; mqttClient is undefined.
The unrelated mqtt_client field from the base class doesn't change anything.
So, I've created "interface class" with all static methods, which I want to expose to hscript scripts. It looks like this:
package com.application.interfaces.Terrain;
import com.application.TerrainCore
class Terrain {
private static var terrain:TerrainCore;
public static function _init(inTerrain:TerrainCore):Void {
terrain = inTerrain;
}
public static function test(s:Int):Void {
terrain.test(s);
}
}
The problem is, that I need to set terrain object somehow, but I don't want it to be exposed to scripts. I expose whole classes with
var interp = new Interp();
var module = Type.resolveClass("com.application.interfaces.Terrain");
interp.variables.set("Terrain", module)
The idea was to override method call in hscript.Interp so it doesn't execute any method named _init, but I don't know how to do that. Original call method looks like this:
function call( o : Dynamic, f : Dynamic, args : Array<Dynamic> ) : Dynamic {
return Reflect.callMethod(o,f,args);
}
Can you use a class instance of Terrain instead of using static members? Eg:
interp.variables.set("Terrain", new Terrain(new TerrainCore()));
Script users wont know if they are using static or instance methods as it will still be access via:
Terrain.test(123);
in script.
Another option (based on clemos), is to use rtti to work out what is allowed (instead of maintaining a list of it), eg:
Terrain._init(new TerrainCore());
_init is a private function now, so you need to #:allow it from your calling class (see below), also, you need to annotate with #:rtti so you can grab info about the functions at runtime, so Terrain now looks like:
#:rtti
class Terrain {
private static var terrain:TerrainCore;
#:allow(test.hscript.demo.Main)
private static function _init(inTerrain:TerrainCore):Void {
terrain = inTerrain;
}
public static function test(s:Int):Void {
terrain.test(s);
}
}
Finally, the script interp fcall now honours whether a field is public or private, ie:
public override function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic
var rtti = haxe.rtti.Rtti.getRtti(o);
for (field in rtti.statics) {
if (field.name == f && field.isPublic == false) {
error(EInvalidAccess(f));
}
}
return super.fcall(o, f, args);
}
Its worth noting that I used statics rather than fields for obvious reasons. Im also not sure what overhead this would cause with the loop and the rtti.
I believe it's fcall you should override, as call is used for toplevel calls only :
https://github.com/HaxeFoundation/hscript/blob/master/hscript/Interp.hx#L328-L331
It should be easy to filter f argument like :
if ( FORBIDDEN_FIELDS.indexOf( f ) > -1 ) throw EInvalidAccess( f );
or
if ( f.indexOf('_') == 0 ) throw EInvalidAccess( f );
I'm trying to use Nape with HaxeFlixel. Sadly, there's almost no documentation on how to use the addons.nape package and I just can't figure out why this code isn't moving the white rectangle (_test). (I left out imports for simplicity)
class PlayState extends FlxNapeState
{
var _test = new FlxNapeSprite(16, 16);
override public function create():Void
{
super.create();
_test.makeGraphic(16, 16);
_test.body.type = BodyType.KINEMATIC;
add(_test);
}
override public function update():Void
{
_test.body.velocity.x = 100;
super.update();
}
}
There are two issues with your code:
Directly initializing the _test variable leads to the FlxNapeSprite constructor call happening in the constructor of your PlayState. create() is called after the state constructor. This can cause crashes and otherwise weird behavior since Flixel does its internal cleanup between the constructor call of the new state and create() (graphics are disposed, for example, and in this case the Nape Space instance doesn't exist yet since it's created in the super.create() call).
The FlxNapeSprite constructor has a createRectangularBody argument which defaults to true and calls the function of that same name if true. Since you're not passing any asset to the constructor, it ends up creating a Shape with a width and height of 0. This leads to the following error:
Error: Cannot simulate with an invalid Polygon
Instead, you'll want to call createRectangularBody() manually after makeGraphic() to create a Shape that matches the graphic's dimensions.
The complete, working code looks like this:
package;
import flixel.addons.nape.FlxNapeSprite;
import flixel.addons.nape.FlxNapeState;
class PlayState extends FlxNapeState
{
override public function create():Void
{
super.create();
var _test = new FlxNapeSprite(16, 16);
_test.makeGraphic(16, 16);
_test.createRectangularBody();
_test.body.velocity.x = 100;
add(_test);
}
}
Regarding documentation, the FlxNape demo is a great resource to learn from.