Typescript Child Namespaces and Ambient Modules - javascript

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

Related

What is the purpose of a `getInstance()` method?

I am looking at a user service, my understanding is it's similar to a user service in Nest, but not really.
In it I see the following:
export class UsersService {
private usersDao: UsersDao
constructor() {
this.usersDao = UsersDao.getInstance();
}
}
static getInstance(): UsersService {
if (!UsersService.instance) {
UsersService.instance = new UsersService();
}
return UsersService.instance;
}
What is that getInstance() doing exactly? And why not just:
export class UsersService {
constructor(private usersDao: UsersDao) {}
}
What is the goal of getInstance()?
Usually this is part of the singleton pattern. Basically one class that, once instantiated, any subsequent classes will refer to that instance, rather than creating a fresh instance each time.
https://en.wikipedia.org/wiki/Singleton_pattern
Its useful for a class where something complex needs to happen when it is first constructed, but all following calls just need access to the properties.
I'd also like to mention that you can (in JavaScript specifically) export an instance, and all modules that import the module will have access to the same instance.

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)

Types have separate declarations of a private property

I am learning Angular (which is written in TypeScript) and I stumbled upon this error:
Class 'SnackbarService' incorrectly extends base class 'MatSnackBar'.
Types have separate declarations of a private property '_overlay'.
when trying to extend MatSnackBar from #angular/material.
This is my code:
import { MatSnackBar } from '#angular/material';
import { Overlay } from '#angular/cdk/overlay';
import { LiveAnnouncer } from '#angular/cdk/a11y';
...
export class SnackbarService extends MatSnackBar {
constructor(
private _overlay: Overlay,
private _liveAnnouncer: LiveAnnouncer,
...
) {
super(_overlay, _liveAnnouncer, ...);
}
}
}
Any help with any type of explanation on why this happens would be really be appreciated.
This happens because by declaring the constructor as taking a private _overlay parameter, you have created your own _overlay, but that is already defined in the base class MatSnackBar.
Remove the private part from the declaration and inherit it from the base class. Do the same for the other constructor parameters.
export class SnackbarService extends MatSnackBar{
constructor(
_overlay: Overlay,
_liveAnnouncer: LiveAnnouncer,
...
) {
super(_overlay, _liveAnnouncer, ...);
}
}
}
You can still access them via this.
This also happens in cases, were dependencies version mistatch. [From typescript perspective]
For example if your app A uses version 1.0 of a package B, and version 1.0 of package C. However package C uses a different version of package B, say 2.0.
Now you have two different classes with same name in overall build process.
To resolve it you must get package C upgraded or package B upgraded, so to have same version accross the App.

Typescript Class Becoming Empty

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 };

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.

Categories