Typescript Class Becoming Empty - javascript

Given a specific setup in typescript I end up at runtime with empty class objects in javascript. The setup is simply 2 classes referencing each other will equal one of them being empty in the scope of the other. I know how to avoid it (by introducing a third class in between the 2) but I'm curious as to why it happens and is there a way to still make those cross references and still have a working code. I tried tons of setup to try to make it work so here's what I found.
Setup: each class in its own module (module names and class names irrelevant)
ClassA is our main entry:
// ClassA
import ClassB = require('ClassB');
import ClassC = require('ClassC');
class ClassA
{
constructor()
{
console.log(ClassC.TEST);// shows 1
new ClassB();// shows null
console.log(ClassC.TEST);// shows 1
}
}
export = ClassA;
// ClassB (in different module)
import ClassC = require('ClassC');
class ClassB
{
public static ClassBRef:any;
constructor()
{
console.log(ClassC.TEST);// in this scope ClassC is an empty object with no properties
}
}
export = ClassB;
// ClassC (in different module)
import ClassB = require('ClassB');
class ClassC
{
public static TEST:number = 1;
constructor()
{
ClassB.ClassBRef;
}
}
export = ClassC;
Because ClassB references ClassC and ClassC references ClassB here's the result: ClassC within ClassA scope exist without problem but within ClassB scope, ClassC is an empty object at runtime with no properties. In other words in typescript everything is fine but javascript will not have it. Changing module names, location, class names, using static or instance scope, in constructor or instance methods or static methods, etc .. makes no difference and ClassC is always empty in ClassB scope.
Like I said the fix is to have a third class handling communication with the 2 faulty classes (so at least one of the 2 classes has NO references to the second one) but again my question is, how to do it without needing a third class and without removing the cross reference between the 2 classes?

You have a circular reference between ClassB and ClassC. In any JavaScript environment it's really hard to resolve these problems.
You have two scenarios basically.
ClassB is required.
ClassC is required.
ClassC attempts to require ClassB.
In this case since ClassB is still being built (it hasn't exported anything) then it will be undefined. The reverse can happen if ClassC is loaded first.
There is a way you can solve it, without extra files however it "feels" wrong.
import containerC = require('ClassC');
// or import * as containerC from 'ClassC'; in ES6 syntax
export class ClassB
{
public static ClassBRef:any;
constructor()
{
console.log(containerC.ClassC.TEST);// in this scope ClassC is an empty object with no properties
}
}
// ClassC
import containerB = require('ClassB');
export class ClassC
{
public static TEST:number = 1;
constructor()
{
containerB.ClassB.ClassBRef;
}
}
This is possible because the value returned by require will be populated by the time the constructor of either class runs.
You cannot use export = X syntax, you must use the named export syntax. You also cannot try to access either class until after the module loads. So it must be in a constructor or function that is called after the class is exported.

Use the syntax
import { ClassC } from "./ClassC"; for importing the classes, instead of import ClassC = require('ClassC');
Class B:
import { ClassC } from "./ClassC"; // use this syntax
class ClassB
{
public static ClassBRef:any;
constructor()
{
console.log(ClassC.TEST);
}
}
export { ClassB };
Class C:
import { ClassB } from "./ClassB"; // use this syntax
class ClassC
{
public static TEST:number = 1;
constructor()
{
ClassB.ClassBRef;
}
}
export { ClassC };

Related

How to properly export two classes on a single index.js file using Javascript

I have two different classes:
Class1.js
class Class1 {
initialize = () => {
console.log("Class1 Initializer");
}
}
export default Class1;
Class2.js
class Class2 {
initialize = () => {
console.log("Class2 Initializer");
}
}
export default Class2;
classes/index.js
import Class1 from "./Class1";
import Class2 from "./Class2";
export default {
Class1,
Class2
}
Then I'm trying to use the classes:
server.js
import { Class1, Class2 } from "./classes";
...
Class1.initialize() <<<<==== ERROR - Cannot read property `initialize` of undefined.
What is the correct syntax of the imports and exports? Why is that error happening?
My understanding is, your method of exporting is possible and that this problem is caused by initialize() being an instance method of Class1. You'll need to create an instance of Class1 before calling initialize(). One way to instantiate Class1 would be via new:
import { Class1, Class2 } from "./classes";
// Create "new" instance of Class1, and then call initialize() on it
(new Class1()).initialize()
Couple things here.
First, you're using arrow syntax in your class methods. Arrow functions don't bind to their own "this" the same way that normal functions do. I'd recommend refactoring that code to mirror below. Do the same for both Class1 and Class2.
class Class1 {
initialize() {
console.log("Class1 Initializer");
}
}
Two, if you're using the ES6 export syntax, you can only ever have one default. In your index.js file, you'll want to just to export { Class1, Class2}, but you can't use export default { Class1, Class2 }.
Three, you need to create an instance of the class before you can call its method. You can either create a new variable that initializes a new instance of the class, then call the method, or you can do it all in-line.
new Class2().initialize();
Fourth, you're importing from a file called a file called "./classes". As far as I can tell that doesn't exist. It should be from "./index".
Hope this helps!
You are using default on object literal. You cant do that.
Do below in classes/index.js:
import Class1 from "./Class1";
import Class2 from "./Class2";
export {
Class1,
Class2
}
and then use :
import * as Classes from "./classes";
Then :
new Classes .Class2().initialize();
By the way how are running above example? its like node server.js?? Node 8.5.0 + supports ES6 like import and export style and file extension also should be .mjs otherwise .js file will need to use require function

Add new JS class and template to aurelia project

I am new to Aurelia, but am working on an existing project and learning as I go.
I want to add a javascript class file and have NPM include it in the build, but I cannot find clear documentation on how to do that.
It is not a complex class and does not require an html template.
Just add the .js file containing your class to your src folder and add the following to import it in the file you intend to use the class.
import * as MyClass from './my-classs';
following the convention, you can create a class with the below...
export class MyClass {
myProperty = 'foo';
myOtherProperty = { key:'value' };
constructor() {
// constructor stuff (optional of course)
}
myMethod() {
// do something
}
}
Then in your ViewModel, for example...
import { inject } from 'aurelia-framework';
import { MyClass } from 'path/to/my-class';
#inject(MyClass)
export class MyViewModel {
constructor(myClass){
this.myClass = myClass;
}
attached() {
console.log(this.myClass.myProperty);
this.myClass.myMethod();
}
}
The way you import your class does depend on whether it's written as a module or not. If it's not a module, you'll have to write it out as #john-little mentioned.
The MyClass will automatically be a singleton until you make it transient (see https://aurelia.io/docs/fundamentals/dependency-injection#object-lifetime-child-containers-and-default-behavior)

ES6: Emitting events from static methods

What is the best way, or is there a best practice or workaround, to emit an event from a static method call?
Let's say I have a ES6 class that calls upload and uploads files in a directory recursively to some endpoint and I want an event emitted after each individual file is successfully uploaded. I know I can have the class inherit EventEmitter, but the .on and .emit functions don't exist without instantiating a new instance of the class. Is there any way around this?
There is no way around it. If you want to call .emit(), then you need an instance of EventEmitter somewhere that you can call the .emit() on. And, of course, this makes sense because your other code has to have something to call .on() with to register listeners on.
If you don't need a separate emitter for every object, you can make just one shared emitter that you either store in some other object, in some useful scope or in module scope or you can even make the single emitter be a class static. If the emitter instance is a class static (initialized at startup), then the static methods could all reference it.
jfriend00 is right, there is no way around without creating an instance of EventEmitter, but using the Singleton pattern it is possible to implement a solution to your problem, for example:
This is the singleton "eventEmitterHandler.ts"
import {EventEmitter} from "events";
export default class EventEmitterHandler extends EventEmitter {
private static instance: EventEmitterHandler;
private constructor() {
super();
}
static getInstance() {
if (!EventEmitterHandler.instance) {
EventEmitterHandler.instance = new EventEmitterHandler();
}
return EventEmitterHandler.instance;
}
}
The class with the static method
import EventEmitterHandler from "./eventEmitterHandler";
export default class ClassA {
constructor() {}
public static staticMethod() {
EventEmitterHandler.getInstance().emit('wave');
}
}
The class with the subscription to the event
import EventEmitterHandler from "./eventEmitterHandler";
export default class ClassB {
constructor() {
EventEmitterHandler.getInstance().on('wave', () => {
console.log('constructor wave');
});
}
}

Import namespace so prefix is not used

I have a library that is in a name space, somewhat like this:
namespace MyLib {
export class MyClass {}
}
I then have another class that is not in the namespace that looks like this:
class Test extends MyLib.MyClass {}
Is there a way for me to access MyClass without having to prefix the namespace? Maybe something like this:
import MyLib;
class Test extends MyClass {}
Edit
I found out that I can do this:
import MyClass = MyLib.MyClass;
Which is okay, but based on that is there anyway for me to import all classes in the namespaces?
The namespacing convention is a C#-ism that doesn't translate very well to JavaScript. My suggestion would be to stop using it altogether because it'll cause more headaches than solve actual problems.
Instead, you should treat your modules themselves as namespace "things". You could do this:
// MyLib.ts
class MyClassA {}
class MyClassB {}
export {
MyClassA,
MyClassB,
};
// Your code
import { MyClassA, MyClassB } from "./MyLib.ts";
Or in separate files:
// MyClassA.ts
export class MyClassA {}
// MyClassB.ts
export class MyClassB {}
// MyLib.ts
import MyClassA from "./MyClassA";
import MyClassB from "./MyClassB";
export {
MyClassA,
MyClassB,
};
// Your code
import { MyClassA, MyClassB } from "./MyLib.ts";
You can't "import all names" though, not in the way you'd need in C# where things are just magically scoped. They need to be declared somehow. On your example code, there's no way to tell that Test comes from MyLib - indeed, even though you could make it work (through global variables, e.g. window.Test = Test), TypeScript wouldn't be happy about it as it wouldn't be able to find a definition for Test, you'd need to declare var it somewhere.

Typescript Child Namespaces and Ambient Modules

I've got a conundrum for you. I'm fairly new to Typescript, but I've enjoyed the experience thus far. Wanting to be a good developer and logically group my modules into namespaces I've come across a rare? issue. Here's the simplest example to recreate the problem.
Lets say you got a class... we'll call it ClassA, which references a class aptly named ClassB which resides in a child namespace.
namespace Foo {
export class ClassA {
public classB: Bar.ClassB;
public constructor() {
}
}
}
and
namespace Foo.Bar {
export class ClassB {
public constructor() {
}
}
}
Typescript couldn't be happier with this. Well when I want to import things for ClassA, I'll put it inside the namespace like so:
namespace Foo {
import SomeClass = SomewhereElse.SomeClass;
export class ClassA {
public classB: Bar.ClassB;
public constructor() {
}
}
}
Again... we cool. But what if I wanted to import an ambient module?
namespace Foo {
import * as SockJS from "sockjs-client"; // TS1147 GRUMBLE! CAN'T DO DAT HERE!
export class ClassA {
public classB: Bar.ClassB;
public constructor() {
}
}
}
Ok, that's fine. I'll just move it outside the namespace like it's telling me to.
import * as SockJS from "sockjs-client";
namespace Foo {
export class ClassA {
public classB: Bar.ClassB; // TS2503:Now you're just a Bar that I used to know
public constructor() {
}
}
}
Suddenly Typescript gets amnesia and doesn't know what Bar is. It doesn't recognize child namespaces. I've tried several ways to make it remember like nesting the namespaces or exporting them, but nothing will make it recognize this. What's the dilly yo? Any of you cool cats know what's going on or how I can resolve it and keep my child namespaces?
Suddenly Typescript gets amnesia and doesn't know what Bar is. It doesn't recognize child namespaces. I've tried several ways to make it remember like nesting the namespaces or exporting them, but nothing will make it recognize this. What's the dilly yo
Its because namespaces are global. And modules are modular. So as soon as you import a module into your file, your file becomes a module. https://basarat.gitbooks.io/typescript/content/docs/project/modules.html
Suggestion
Please use modules. There are plenty of reasons : https://github.com/TypeStrong/atom-typescript/blob/8d43dd1b930a6df0ce62454a1560acfb7eee24c9/docs/out.md

Categories