ES6 Map an object to a decorator - javascript

I'd like to map an object with properties (key) to a decorator (value). I'd like to use a Weak Map if possible. I have a solution that is working using a string, which is fine except that Weak Maps do not accept strings as keys. Is this possible with a Map or a WeakMap?
'use strict';
class Accordion {
constructor() {}
}
let Decorators = new Map();
Decorators.set({nodeName: 'tag-name-here', component: 'accordion'}, (client) => { return new Accordion(client) });
class Client {
constructor() {
let key = {nodeName: 'tag-name-here', component: 'accordion'}
let decorator;
if (Decorators.has(key)) {
decorator = Decorators.get(key)(this);
}
console.log(decorator); //undefined, unless I use a string as a key.
}
}
new Client();

It does not work because different instance of key: {nodeName: 'tag-name-here', component: 'accordion'} will map to a new memory location each time, so you won't be able to get your desired value this way. To make it work, you have to set it to a new variable, so that your code looks like the following:
'use strict';
class Accordion {
constructor() {}
}
let Decorators = new Map();
const key = {nodeName: 'tag-name-here', component: 'accordion'};
Decorators.set(key, (client) => { return new Accordion(client) });
class Client {
constructor() {
let decorator;
if (Decorators.has(key)) {
decorator = Decorators.get(key)(this);
}
console.log(decorator); // this should return an object
}
}
new Client();

Related

nested properties on a class instance

I need to build a structure (i need to do it for jestjs, in order to stub a module) that allows me to access a method in the following way:
// I make an instance of a class
const myTest = new Test()
// I access a method in this way
const result = myTest.prop1.prop2.prop3.getAll()
A basic class would look like
class Test2 {
constructor(){}
//getter
getAll(){
return 'Salutes'
}
}
And I can access getAll()
const myTest2 = new Test()
const result = myTest.getAll()
//in this way result contains a string (*salutes*)
So how can I add more properties in the middle? I found something, but in typescript... I need to do it in javascript
Assuming you need exactly the structure you've specified, in Test you'd have to create the object and have any methods you need be arrow functions or bound functions (if they need to access information from the Test instance; your example didn't so it wouldn't matter whether they had the right this). For instance:
class Test {
constructor() {
this.prop1 = {
prop2: {
prop3: {
getAll: () => {
return "Salutes";
},
getAll2() { // Also works
return "Salutes";
},
getAll3: function() { // Also works
return "Salutes";
}
}
}
}
}
}
const myTest = new Test();
const result = myTest.prop1.prop2.prop3.getAll();
console.log(result);
console.log(myTest.prop1.prop2.prop3.getAll2());
console.log(myTest.prop1.prop2.prop3.getAll3());
Example of using information on the Test instance (it's the same except for the constructor parameter and message property):
class Test {
constructor(message) {
this.message = message;
this.prop1 = {
prop2: {
prop3: {
getAll: () => {
// `this` refers to the `Test` instance because
// this is an arrow function
return this.message;
}
// `getAll2` and `getAll3` wouldn't work because
// they would get `this` based on how they're called
// (it would be the `this.prop1.prop2.prop3` object).
}
}
}
}
}
// I make an instance of a class
const myTest = new Test("Salutes encore!");
// I access a method in this way
const result = myTest.prop1.prop2.prop3.getAll();
console.log(result);
If you want to achieve it without modifying the Test2 class, you can use Proxy class and define custom handler for get method:
class Test2 {
constructor(){}
getAll(){
return 'Salutes'
}
}
let instance = new Test2();
const handler = {
get: function(target, prop, receiver) {
if (['prop1', 'prop2', 'prop3'].includes(prop)) {
return receiver;
}
return Reflect.get(...arguments);
}
};
const proxy = new Proxy(instance, handler);
console.log(proxy.prop1.prop2.prop3.getAll());
console.log(proxy.prop7);
console.log(proxy.getAll());

How to create Class dynamically in a proper way in Typescript?

I am trying to migrate my app to typescript. I have kind of a base class that is my base library object. My classes created dependent on the base class. Below is a glimpse of my problem.
Below code works but autocomplete doesn't work. Couldn't figure out what should be defined for the type of Model.
const map = new WeakMap();
function weakRef<T>(context: T): T {
// #ts-ignore
if (!map.has(context)) { map.set(context, {}); }
// #ts-ignore
return map.get(context);
}
function getModel(provider: Provider) {
return class Model {
getSomething(key: string) {
return weakRef(provider).configuration(key);
}
};
}
class Provider {
configuration: (key: string) => string;
constructor() {
weakRef(this).configuration = (key: string) => {
return key;
};
weakRef(this).Model = getModel(this);
}
get Model(): any {
return weakRef(this).Model;
}
set Model(model: any) {
weakRef(this).Model = model;
}
}
const provider = new Provider();
const obj = new (provider.Model)();
console.log(obj.getSomething('test')); // This works, but autocomplete doesn't
I don't want to pass provider to the constructor in base model.
Any help is appreciated, thanks.
You can get the return type of a function type with ReturnType<T>.
In this case, ReturnType<typeof getModel> should do the trick.
You can also fix the //#ts-ignore lines by constraining T to be an object type.
const map = new WeakMap();
function weakRef<T extends object>(context: T): T {
if (!map.has(context)) { map.set(context, {}); }
return map.get(context);
}
That said, I think this architecture is unnecessarily complicated. Why not just use this instead of a value tied to this?

Convincing TS that an object coming through a proxy is never null

I have the following fn that creates a proxy:
const getStorageProxy = (store?: Storage) => {
const proxyObj: { store?: DataStorage } = {};
return new Proxy(proxyObj, {
get(obj, prop) {
if (!obj.store) {
obj.store = new DataStorage(store);
}
return obj.store[prop];
},
});
};
And then I export several instances of this proxy:
const storageA = getStorageProxy(storeA);
const storageB = getStorageProxy(storeB);
export { storageA, storageB };
The reason for this is that I don't want to actually instantiate the DataStorage class unless it's used, and for convenience I want to be able to type:
import { storageA } from 'storage';
This all works. The problem is that according to TS both of my exported storage instances are of type DataStorage | void because of:
const proxyObj: { store?: DataStorage } = {};
They're actually not because of the getter in the proxy, but how do I tell this to TS without actually assigning something to proxyObj.store on instantiation?

ES2015 - What are the downsides to using static methods which new up a constructor on every call?

I am writing a simple ORM / wrapper for knex.js and there are two approaches I am trying to decide between.
The first approach is just a regular class which must be instantiated before use:
class Base {
constructor() {
this.db = db;
}
all() {
return this.db(this.table);
}
find(id) {
return this.db(this.table).where('id', id).first();
}
// more similar methods...
}
class User extends Base {
constructor() {
super();
this.table = 'users';
}
}
// calling code
let user = new User();
user.find(1).then(user => console.log(user));
The second approach, which I am more inclined to use, uses static methods which create the instance:
class Base {
constructor() {
this.db = db;
}
static all() {
const model = new this;
return model.db(model.table);
}
static find(id) {
const model = new this;
return model.db(model.table).where('id', id).first();
}
}
// calling code
User.find(1).then(user => console.log(user));
I like the static approach because the API is cleaner (no instantiating necessary) but I am not familiar with what the drawbacks are. So my questions are is this a good approach or not? And if not, why? Is there a third option that lies somewhere in the middle that would be better?
how about this if you wanna avoid objects?
class Base {
constructor() { }
static all() {
return db(this.table())
}
static find(id) {
return db(this.table()).where('id', id).first()
}
}
class User extends Base{
constructor() {
super()
}
static table(){
return 'users'
}
}
// calling code
User.find(1).then(user => console.log(user));
this would work, but I don't this is good way to do things, for starters, Base.all() would throw error, also if all your class methods are going to static, you might as well use plain js objects like:
let Base = {
all: function(){
return db(this.table)
},
find: function(id){
return db(this.table.where('id', id).first()
}
}
, extend = (a,b) => {for(let i in b) a[i] = b[i]}
, User = {table: 'users'}
// calling code
User.find(1).then(user => console.log(user));

Angular2 call observer next from child object

I'll try to explain this as best I can.
I have a service that contains an observable class that performs tasks to update itself. That observable class needs to be pushed out to the app using the observer located in the service. How can I call that observer from the child without creating some sort of dependency loop?
Here is a rough example:
class MyService {
subClass$: Observable<SubClass>;
_subClassObserver: Observer<SubClass>;
constructor(private _subClassStore: SubClass){
this.subClass$ = new Observable(observer => {
this._subClassObserver = observer
}).share();
}
pushData(){
this._subClassObserver.next(this._subClassStore)
}
}
class SubClass {
displayData: string;
displayData2: number;
constructor(){
socket.on('setData', function(obj){
this.displayData = obj.dd1;
this.displayData2 = obj.dd2;
//How to call pushData() in MyService from here to push data to app?
}
}
}
_subClassStore is updating through a stream coming in from socket.io. How do I let MyService know when the SubClass data changes so that it can push it using _subClassObserver.next(_subClassStore)?
EDIT:
I added more details to the example above to show how they are related and utilized.
SubClass is just a listener for a stream of data coming from socket.io and saving the information into the class. It starts listening when MyService is constructed.
The goal of MyService is to provide a bunch these sub classes that can be subscribed to across the app. Each one would allow access to a different data stream and the associated data, but all would be contained within a single service.
The question is how to call the pushData() function in the parent so that it keeps the stream updated for subscribers in the app.
Edit 2:
This might help. below is how it would be written as a service without the sub class. The only reason why I'm not doing this is because there are a substantial amount of these listeners being stored to Observables and abstracting them out into classes makes the information much easier to manage but pushing it to the app is what I can't figure out:
class MyService {
class1$: Observable<DataStream>;
_class1Observer: Observer<DataStream>;
_class1Store: DataStream;
constructor(){
this._class1store = {displayData: 'hello', displayData2: 0};
this.class1$ = new Observable(observer => {
this._class1Observer = observer
}).share();
socket.on('setData', function(obj){
this._class1Store.displayData = obj.dd1;
this._class1Store.displayData2 = obj.dd2;
this._class1Observer.next(this._class1Store)
}
}
interface DataStream = {
displayData: string;
displayData2: number;
}
Instead of function(obj) use ()=> otherwise this won't ponit to the MyService instance.
constructor(){
socket.on('setData', (obj) =>{
this.displayData = obj.dd1;
this.displayData2 = obj.dd2;
//How to call pushData() in MyService from here to push data to app?
}
}
I'm not sure but I think socket is prone to run outside Angulars zone.
Try also
constructor(zone:NgZone){
socket.on('setData', (obj) =>{
zone.run(() => {
this.displayData = obj.dd1;
this.displayData2 = obj.dd2;
//How to call pushData() in MyService from here to push data to app?
});
}
}
To be able to call a method in MyService from SubClass, SubClass needs a reference to MyService
class MyService {
subClass$: Observable<SubClass>;
_subClassObserver: Observer<SubClass>;
constructor(private _subClassStore: SubClass){
_subClassStore.myService = this;
this.subClass$ = new Observable(observer => {
this._subClassObserver = observer
}).share();
}
pushData(){
this._subClassObserver.next(this._subClassStore)
}
}
class SubClass {
displayData: string;
displayData2: number;
myService:MyService;
constructor(zone:NgZone){
socket.on('setData', (obj) =>{
zone.run(() => {
this.displayData = obj.dd1;
this.displayData2 = obj.dd2;
this.myService.pushData();
});
}
}
}

Categories