Typescript, cant load internal module - javascript

I m actually learning typescript, and I m facing some problems with internal modules.
In fact, I have three files :
index.ts in which I start my app
///<reference path='RouteManager.ts'/>
import RouteManager = RestifyRouting.RouteManager;
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts that manage my REST routes
///<reference path='RouteParser.ts'/>
module RestifyRouting {
export class RouteManager {
routeParser:RouteParser;
constructor() {
}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
}
RouteParser which has to parse some string to get some informations
module RestifyRouting {
export class RouteParser {
constructor() {
}
public register(path, callback) {
console.log('super register');
}
}
}
I have a gulp file that creates my .js and d.ts files and it works great, except for the index.js file. The compiler tells my that RestifyRouting (which is my internal module) is undefined and I dont know why...
Can you help me ?
PS : every files are in the same folder, it's just a learning application.
Thanks for advance

As of TypeScript 1.5 the module syntax is aligned with ES6 module syntax and that is what I have been using as well...
You can remove any references to TypeScript modules and just export the classes directly
index.ts
import { RouteManager } from './RouteManager';
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts
import { RouteParser } from './RouteParser';
export class RouteManager {
routeParser:RouteParser;
constructor() {}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
RouteParser.ts
export class RouteParser {
constructor() {}
public register(path, callback) {
console.log('super register');
}
}
Keeping modules
If you'd like to keep using internal modules then you have to be sure to export your module as well as the classes inside the module.
// RouteManager.ts
export module RestifyRouting {
export class RouteManager{}
}
//index.ts
import { RestifyRouting } from './RouteManager';
//usage
var manager = new RestifyRouting.RouteManager();
Something to keep in mind is that you will not be able to import multiple items into the the same name.
// i.e.
import { RestifyRouting } from './RouteManager';
import { RestifyRouting } from './RouteParser';
NOTES
the {} syntax in the import statement can allow multiple imports
{ Class1, Class2 }
The {} can be skipped if you exporting a default:
//Source (foo.ts):
export default class Foo{}
//Reference:
import Foo from './foo';
//usage:
class User {
foo: Foo;
}

Related

ReferenceError: Cannot access 'Player' before initialization

So I've been using ES6 style syntax with import/export on Nodejs with the ESM module loader. Everything has been fine until I started getting an error pertaining to imports.
Here's the error messages:
joseph#InsaneMachine:~/placeholder2/main-server$ npm start
> main-server#1.0.0 start /home/joseph/placeholder2/main-server
> nodemon --experimental-modules src/index.mjs
[nodemon] 1.19.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node --experimental-modules src/index.mjs`
(node:16942) ExperimentalWarning: The ESM module loader is experimental.
file:///home/joseph/placeholder2/main-server/src/games/game-player.mjs:3
export default class GamePlayer extends Player
^
ReferenceError: Cannot access 'Player' before initialization
at file:///home/joseph/placeholder2/main-server/src/games/game-player.mjs:3:41
at ModuleJob.run (internal/modules/esm/module_job.js:109:37)
at async Loader.import (internal/modules/esm/loader.js:132:24)
[nodemon] app crashed - waiting for file changes before starting...
Here are the files Player (Base class):
import PasswordHash from 'password-hash';
import GamesService from '../games/games.service.mjs';
import PlayersService from './players.service.mjs';
import QueueingService from '../queueing/queueing.service.mjs';
export default class Player
{
constructor(object)
{
Object.assign(this, JSON.parse(JSON.stringify(object)));
}
get id()
{
return this._id.toString();
}
equals(other)
{
if(other.id != null)
return other.id == this.id;
return false;
}
checkPassword(password)
{
return PasswordHash.verify(password, this.password);
}
online()
{
return PlayersService.consumer.isPlayerOnline(this);
}
inQueue()
{
return QueueingService.queued(this);
}
inGame()
{
return GamesService.getActiveGameByPlayer(this) != null;
}
reduce()
{
return {
id: this.id,
username: this.username,
email: this.email,
admin: this.admin,
online: this.online(),
in_queue: this.inQueue(),
in_game: this.inGame(),
};
}
static hashPassword(password)
{
return PasswordHash.generate(password);
}
static schema = {
username: String,
password: String,
email: String,
email_confirmed: Boolean,
admin: Boolean,
}
}
And GamePlayer (Child Class):
import Player from '../players/player.mjs';
export default class GamePlayer extends Player
{
constructor(player, token)
{
super(player);
this.token = token;
}
}
And the heirarchy of the project:
src/
-- games/
-- -- game-player.mjs
-- -- ...
players/
-- -- player.mjs
-- -- ...
-- ...
How can I fix this import issue, unless this is something else?
Edit: I am not using Babel as far as I know, I am using --external-modules provided by Node. Not sure how that works.
I went to the Node.JS forums and asked what could be the issue. Not a babel issue at all, just circular dependencies. For example:
// A.js
import B from './B.js'
export default class A{}
// B.js
import A from './A.js'
export default class B extends A{}
Sorry there wasn't nearly enough information to be able to figure this one out. I got a lot of help on the node.js github and someone looked through my project on github and ended up finding an instance where two modules pointed at each other.
The dependencies in your imports were probably too difficult to resolve, so it gave up, leaving you with Player uninitialized at the point where it is needed to define GamePlayer.
As I mentioned in a comment for another answer, import can be used in a "circular" way, but Node.js can't always untangle the dependencies.
In my case it had no problem with a class in one file and a subclass of that in another file, where both of them import each other, and it's hard to say exactly where it got too complicated, but this is a simplified version of what I had that broke it:
// server.js
import Acorn from './acorn.js';
import Branch from './branch.js';
class Server {
...
}
// universe.js
import Acorn from './acorn.js';
import Branch from './branch.js';
import Thing from './thing.js';
export default class Universe {
things(type) {
if (Thing.klass[type]) {
...
}
}
...
}
// acorn.js
import Thing from './thing.js';
export default class Acorn extends Thing {
...
}
// branch.js
import Thing from './thing.js';
export default class Branch extends Thing {
...
}
// thing.js
import Acorn from './acorn.js';
import Branch from './branch.js';
export default class Thing {
static klass(type) {
const klass = {acorn: Acorn, branch: Branch};
...
return klass;
}
constructor(...) {
this.type = this.constructor.name.toLowerCase();
...
}
...
}
I'm using the same code for browsers and server-side Node.js, so I was also transpiling it with Babel, which handled everything fine. But Node may have other constraints that make it more difficult, because, at least as far as importing is concerned, it was on a different track from browsers (and others), and is now trying to bridge the gap. And also the knot may be more tangled than it appears to the naked eye.
In the end I ditched the most circular part of my pattern, which was the part where I refer to the Thing subclasses within Thing itself. I extracted that into a "factory-like" pattern, where the factory knows about the Thing subclasses, but the Thing and its subclasses don't need the factory.
Assigning a module in the initialization phase requires initialization of all module dependencies. If at least one dependency creates a cycle and accesses such dependencies (execution, assignment, apply), then initialization will not occur.
During loops (when modules refer to each other), you cannot use (assignment, execution, launch) such modules at the stage of initialization of these modules.
For the correct module code, you must first:
A dependency must be fully initialized before it can be used (execute, assign, apply);
Declare a module script (a script that is not executed, including excludes the assignment of other modules);
In a separate script, launch its execution or its configuration after initialization;
To fix the situation, you need to fix the dependency that creates the loop (for example, create a getter for it);
First import modules that use dependencies for initialization Example:
// ./script.js
import B from './B.js';// this it first, because it uses a dependency (extends A).
import A from './A.js';
// ./A.js
import B from './B.js';
export default class A{}
// ./B.js
import A from './A.js';
export default class B extends A
{
}
Use a separate export file for multiple dependencies Example:
// ./export.js
//In the export.js file, the order of declaration matters. Declare independent modules first. Then declare modules with dependencies.
export {default as A} from './A.js';
export {default as B} from './B.js';
// ./A.js
import {B} from './export.js';
export default class A {}
// ./B.js
import {A} from './export.js';
export default class B extends A{}
##Examples
The examples clearly show how to solve the problem of loops.
The main thing to understand is that module dependencies must be used implicitly during initialization or used after initialization.
./run_script.js
export B from './B.js'; // the first, since it has a dependency A ( extends A)
export A from './A.js';
Working script
./A.js
import B from './B.js';
export default class A {
}
./B.js
import A from './A.js';
export default class B {
}
not working script (loop)
./A.js
import B from './B.js';
export default class A {
}
A.dependency={BClass:B};
./B.js
import A from './A.js';
export default class B {
}
B.dependency={AClass:A};
How to fix
./run_script.js
export A from './A.js';
export B from './B.js';
A.dependency={BClass:B};
B.dependency={AClass:A};
./A.js
import B from './B.js';
export default class A {
}
./B.js
import A from './A.js';
export default class B {
}
Hook
./run_script.js
export A from './A.js';
export B from './B.js';
./A.js
import B from './B.js';
export default class A {
}
A.dependency={};
Object.defineProperties(A.dependency,{
BClass:{
get(){
return B;
}
}
});
./B.js
import A from './A.js';
export default class B {
}
B.dependency={};
Object.defineProperties(B.dependency,{
AClass:{
get(){
return A;
}
}
});
Hook2
./init.js
class Init {
#listeners={};
trigger(event){
if(event in this.#listeners){
let listeners=this.#listeners[event];
delete this.#listeners[event];
for(let call of listeners){
call();
}
}
}
on(event,call){
if(!(event in this.#listeners)){
this.#listeners[event]=[];
}
this.#listeners[event].push(call);
}
}
export default new Init();
./run_script.js
export A from './A.js';
export B from './B.js';
./A.js
import init from './init.js';
import B from './B.js';
export default class A {
}
init.on('init_B',()=>{
console.log('init_B');
A.dependency={BClass:B};
init.trigger('init_A');//Must be called because the listeners might not have been initialized before the core trigger
});
init.trigger('init_A');// core trigger
./B.js
import init from './init.js';
import A from './A.js';
export default class B {
}
init.on('init_A',()=>{
console.log("init_A");
B.dependency={AClass:A};
init.trigger('init_B'); //Must be called because the listeners might not have been initialized before the core trigger
});
init.trigger('init_B'); // core trigger
UPDATEd:
Rewrote init.js for comfortable use
class Init {
static #listeners={};
#name;
constructor(name){
this.#name=name;
}
module(name){
return new Init(name);
}
trigger(event){
if(event===undefined){
event=this.#name;
}
if(event in Init.#listeners){
let listeners=Init.#listeners[event];
delete Init.#listeners[event];
for(let call of listeners){
call();
}
}
return this;
}
on(event,call){
if(!(event in Init.#listeners)){
Init.#listeners[event]=[];
}
let sanbox=call;
if(this.#name!==undefined){
sanbox=()=>{
call();
this.trigger(this.#name);
};
}
Init.#listeners[event].push(sanbox);
return this;
}
}
export default new Init();
used
import init from './init.js';
import A from './A.js';
export default class B{
}
B.dependency={};
init.module('B')
.on('A',()=>{
B.dependency.AClass=A;
})
.on ('Last_Dep',()=>{
//...
})
//...
.trigger();
import init from './init.js';
import B from './B.js';
export default class A{
}
A.dependency={};
init.module('A')
.on('B',()=>{
A.dependency.BClass=B;
})
.trigger();
Remove es2015 from your Babel configuration.
class extends native ES6 class and Babel transpiles to ES which is causing the issue most likely

Webpack: reference bundled components from outside bundle

I have a class inside my bundled app that I want to allow users of the app to extend.
Here's what the definition of the bundled class looks like:
import * as d3 from 'd3';
class VizPlugin {
constructor(options) {
this.options = options;
}
build() {
}
...
}
export default VizPlugin;
The application is a full client/server nodejs app, installed on a customers server. After the app is bundled/deployed/installed on the server, I want the customer to be able to extend the app and add their own custom modules as extensions/plugins, like this:
import VizPlugin from './js/viz-plugin'; //there is no viz-plugin.js because it's bundled
class ScatterPlot extends VizPlugin {
constructor(options) {
super(options);
}
build() {
//I'd like to also use the reference to d3 from VizPlugin here
}
...
}
export default ScatterPlot;
They would put their javascript code in a directory along side the other bundled client javascript and import from that. Basically, there needs to be a named file called "viz-plugin.js" that can be imported from "scatter-plot.js".
Add d3 to your class, and give the extender some way of using it:
import * as d3 from 'd3';
class VizPlugin {
constructor(options) {
this.options = options;
this.d3 = d3;
}
useD3 (callback) {
callback(this, this.d3);
}
build() {
}
...
}
module.exports = VizPlugin as VizPlugin;
You can use the webpack SplitChunksPlugin and give names to individual modules by using splitChunks.name.
The following code will look for the viz-plugin.js module and tell it to keep it's name:
optimization: {
splitChunks: {
name(module, chunks, cacheGroupKey) {
const moduleId = module.identifier();
if( moduleId && moduleId.indexOf('viz-plugin.js') >= 0 ) {
return 'viz-plugin'
}
return null;
}
}
},
Now, in the dist/build output, there will be a file called "viz-plugin.js" that you can extend, as such:
import VizPlugin from './dist/js/viz-plugin';
class ScatterPlot extends VizPlugin {
constructor(options) {
super(options);
}
build() {
//I'd like to also use the reference to d3 from VizPlugin here
}
...
}
export default ScatterPlot;

Default export new instance of class

Will the snippets below produce new instance every time it's imported?
// 1st implementation
class ConnectionManager {
...
}
export default new ConnectionManager();
// 2nd implementation
class ConnectionManager {
...
}
const connectionManager = new ConnectionManager();
export default connectionManager;
If yes, how can I get the same instance in every import?
ES6 modules follow single instance pattern. That is, the instance is created when the module is loaded.
Here is an article about it.
// File: yolo.js
class Yolo {}
export let yolo = new Yolo();
// File: laser.js
import { yolo } from "./yolo.js";
// yolo is a single instance of Yolo class
// File: cat.js
import { yolo } from "./yolo.js";
// same yolo as in laster.js
It should be the same.
The following example uses both the implementations, imports them into 2 different files, and imports them all into single index file. Everytime an instance is created, we generate a random value for the class, and log its creation.
// ConnectionManagerImpl1.ts
class ConnectionManagerImpl1 {
public value;
constructor() {
this.value = Math.random().toString(36).substring(7);
console.log(`New ConnectionManagerImpl1 instance created: ${this.value}`)
}
}
export default new ConnectionManagerImpl1();
// ConnectionManagerImpl2.ts
class ConnectionManagerImpl2 {
public value;
constructor() {
this.value = Math.random().toString(36).substring(7);
console.log(`New ConnectionManagerImpl2 instance created: ${this.value}`)
}
}
const connectionManagerImpl2 = new ConnectionManagerImpl2();
export default connectionManagerImpl2;
// import1.ts
import connectionManagerImpl1 from './ConnectionManagerImpl1';
import connectionManagerImpl2 from './ConnectionManagerImpl2';
export { connectionManagerImpl1, connectionManagerImpl2 };
// import2.ts
import connectionManagerImpl1 from './ConnectionManagerImpl1';
import connectionManagerImpl2 from './ConnectionManagerImpl2';
export { connectionManagerImpl1, connectionManagerImpl2 };
// index.ts
import * as import1 from './import1';
import * as import2 from './import2';
console.log(import1)
console.log(import2)
console.log("Done")
Ran the above setup using tsc --module 'commonjs' * && node index.js
Output:
New ConnectionManagerImpl1 instance created: ddt3re
New ConnectionManagerImpl2 instance created: uv5z6
{ connectionManagerImpl1: ConnectionManagerImpl1 { value: 'ddt3re' },
connectionManagerImpl2: ConnectionManagerImpl2 { value: 'uv5z6' } }
{ connectionManagerImpl1: ConnectionManagerImpl1 { value: 'ddt3re' },
connectionManagerImpl2: ConnectionManagerImpl2 { value: 'uv5z6' } }
Done
As you can see, only 1 instance of ConnectionManagerImpl1 and ConnectionManagerImpl2 were created. So, both the implementation should create only 1 instance.
The export statement is used when creating JavaScript modules to export functions, objects, or primitive values from the module so they can be used by other programs with the import statement.
There are two different types of export, named and default. You can have multiple named exports per module but only one default export.
export default class ConnectionManager { .. }
Or
class ConnectionManager {
...
}
export default connectionManager;

Expose node_module using both import and require

I wrote a node module and I'm trying to understand how I would go about allowing users to either require or import the module depending on their environment.
At the moment I'm exposing my classes like this:
/src/index.ts
import { Class1 } from './Class1';
import { Class2 } from './Class2 ';
module.exports = {
Class1,
Class2
}
gets compiled to
/lib/index.js
var Class1_1 = require("./Class1")
var Class2_1 = require("./Class2 ")
module.exports = {
Class1: Class1_1.Class1
Class2: Class2_1.Class2
}
which works allows require but not import
const { Class1, Class2 } = require('my-module');
I've been googling everything I can think of but can't get any examples or pointers.

Reference another internal module implicitly in typescript

Say I have
List.ts:
module Helper {
export class List{
}
}
Parser.ts:
module Helper {
export class Parser {
}
}
Now I have another module, and every time I want to use "List", I need to say Helper.List. Is it possible to just say something like:
import Helper;
module Data {
export interface DataRepository {
getRange() : List<string>;
}
}
So that every time I want to use List, I can just type List instead of List.Helper? I know you can do:
import List = Helper.List;
But is something like
import * from Helper;
possible?
That is only possible when you use external modules (which are recommended). However, you can do the following:
module Helper {
export class List<T> {
}
}
module Helper {
export class Parser {
}
}
module Data {
export interface DataRepository {
getRange() : Helper.List<string>;
}
}
Or
module Data {
type StringList = Helper.List<string>;
export interface DataRepository {
getRange() : StringList;
}
}

Categories

Resources