javascript, remove common parameter from child classes - javascript

I have defined a Axios base class like this
class BaseService {
constructor(baseurl, defaultHeaders, errorCallback) {
const options = {
baseURL: baseurl,
headers: defaultHeaders
};
this.fetcher = axios.create(options);
}
}
class serviceA extends BaseService {}
class serviceB extends BaseService {}
then I am creating multiple instance like this
const serviceA = new ServiceA(IVA_URL,defaultHeaders,setloggedInUser);
const serviceB = new ServiceB(IVB_URL,defaultHeaders,setloggedInUser);
Now I want to remove defaultHeaders,setloggedInUser these two or more common parameters from every service constructor function.
Also defaultHeaders will be available at run time.

You can use static members. set default value once and use multiple times.
class BaseService {
static defaultHeaders;
static defaultCallback;
constructor(baseurl) {
const options = {
baseURL: baseurl,
headers: BaseService.defaultHeaders
};
this.fetcher = axios.create(options);
}
}
class serviceA extends BaseService {}
class serviceB extends BaseService {}
and then:
BaseService.defaultHeaders = defaultHeaders;
BaseService.defaultCallback = setloggedInUser;
const serviceA = new ServiceA(IVA_URL);
const serviceB = new ServiceB(IVB_URL);

Related

Method substitution between objects

In nodejs, typescript, I want to substitute a method of an object with a method of another object; I have written the following very simple example to better understand my problem (the real situation is, more or less, the same):
export default class A_01 {
constructor(private variableA1: string) {}
public writeSomething() {
console.log(`${this.variableA1} from class A`);
}
}
import A_01 from "./oop_class_A";
export default class B_01 extends A_01 {
constructor(private variableB1: string) {
super(variableB1);
}
public writeSomething() {
console.log(`${this.variableB1} from class B`);
}
}
import A_01 from "./oop_class_A";
class C_01 {
constructor() {}
run() {
return new A_01("Object A_01 from class C_01"); // cannot modify this object creation!!!
}
}
import A_01 from "./oop_class_A";
import B_01 from "./oop_class_B";
const D_01 = new A_01("from_class_D_01");
D_01.writeSomething();
So, how to print from_class_D_01 from class B (and NOT from class A) ?
I have tried casting
const D_01 = new A_01("from_class_D_01") as B_01
but it's only a type and I lose it at runtime.
Not sure if this is what you need, this is a very hacky way to overwrite the writeSomething method after an A_01 instance has been created.
const D_01 = new A_01("from_class_D_01")
D_01.writeSomething = B_01.prototype.writeSomething
D_01.writeSomething()
Now it will write "from class B" even though it's an instance of A_01

How can I inherit class in javascript and use static method?

I have this simple js service that I want to use as a static method. Right now here's my code:
class BaseService {
constructor() {
this.baseUrl = '';
}
list(params) {
return axios.get(`${this.baseUrl}`, { params }).then((response) => response.data);
}
//(...)
}
class DimensionService extends BaseService {
constructor() {
super();
this.baseUrl = '/api/dimensions';
}
}
//usage
let service = new DimensionService();
service.list();
and I would rather use it like that:
DimensionService.list()
the problem is I cannot inherit baseUrl, because in the static method the constructor is never called. What can I do?

Cannot Inject Dependency Into Derived Class

I have a relatively simple setup with three classes. I am using inversify for dependency injection. But when injecting the class MessageBroker into the derived class Repository the MessageBroker is undefined:
import 'reflect-metadata';
import { injectable, inject, Container, unmanaged } from 'inversify';
const container = new Container();
const registerProviders = (...providers: any[]) =>
providers.forEach(provider => container.bind(provider.name).to(provider));
const getProvider = (provider): any => container.get(provider.name);
#injectable()
export class MessageBroker {
start = () => console.log('init message broker');
}
#injectable()
export abstract class Repository {
#inject(MessageBroker.name) private mb: MessageBroker;
constructor(#unmanaged() protected readonly user: any) {}
// this.mb is undefined
initialize = () => this.mb.start();
}
#injectable()
export class UserRepository extends Repository {
constructor() {
super({ user: 'some object' });
this.initialize();
}
}
registerProviders(UserRepository, Repository, MessageBroker);
const repo: UserRepository = getProvider(UserRepository);
You can try it yourself. I've created a small GitHub repository: https://github.com/flolude/stackoverflow-inversify-injected-service-undefined
When running the script, I get this error:
/project/index.ts:22
initialize = () => this.mb.start();
^
TypeError: Cannot read property 'start' of undefined
at UserRepository.Repository.initialize (/project/index.ts:22:30)
at new UserRepository (/project/index.ts:29:10)
at _createInstance (/project/node_modules/inversify/lib/resolution/instantiation.js:21:12)
at Object.resolveInstance (/project/node_modules/inversify/lib/resolution/instantiation.js:41:18)
at /project/node_modules/inversify/lib/resolution/resolver.js:72:42
at Object.resolve (/project/node_modules/inversify/lib/resolution/resolver.js:96:12)
at /project/node_modules/inversify/lib/container/container.js:319:37
at Container._get (/project/node_modules/inversify/lib/container/container.js:310:44)
at Container.get (/project/node_modules/inversify/lib/container/container.js:230:21)
at getProvider (/project/index.ts:9:50)
P.S. I get pretty much the same error when compiling the code to Javascript
Your MessageBroker has only been set in memory but has never been instantiated, which is how it is getting the undefined error. In your constructor you will need to set
this.mb = new MessageBroker();
Another way you can do this without the above line is to add a empty parameter signatured constructor into the MessageBroker class.

How to create a singleton observable?

I have a singleton class :
let instance = null;
class SingletonClass {
constructor() {
if (!instance) {
instance = this;
}
return instance;
}
get types() {
return this._types;
}
set types(value) {
this._types = value;
}
}
export default SingletonClass;
and i use it like this:
import SingletonViews from './SingletonClass';
export default class MyStore {
this._singletonClass = new SingletonClass();
#observable types;
this.types= this._singletonClass.types; //i dont know what to do here to bind this two together
}
But whenever this._singletonClass.types updated the types observable wont. what can i do about it?
can i have a #observable singleton or can i bind its proprty to a #observable
Your "singleton" is not an observable itself and what is your intention? just proxy the singleton values?
Since you don't seem to need to initialize your singleton, I'd rewrite it this way:
Singleton
import * as mobx from 'mobx'
const {observable, action} = mobx
class SingletonClass {
#observable types
#action setTypes (value) {
this.types = value;
}
}
export default new SingletonClass()
MyStore
import singletonClass from './SingletonClass'
export default class MyStore {
singletonClass = singletonClass
}
Now you can use your store somewhere in your app and observe your singleton.

How to pass a string or class to a method to create instance

I am to use the following method, it works by passing a type to it such as obj.addComponent(MyClass). This works just fine.
I tried to modify the type parameter by adding | string to it, but it now gives me errors saying:
Cannot use 'new' with an expression whose type lacks a call or construct signature.
Is there anyway for me to modify this so that I can pass either a Class name in or a string version of the class?
Here is what I have that doesn't work:
public addComponent<T extends Component>(type: ComponentType<T> | string): T {
let comp;
comp = new type() as T;
comp.name = comp.constructor.name;
}
Here are its dependencies:
class Component extends Obj {
}
interface ComponentType<T extends Component> {
new(): T;
}
I have tried using Object.create(), which works fine, but then I get a new error:
Uncaught TypeError: Cannot assign to read only property 'name' of object '[object Object]'
Edit:
In the end I would like to be able to pass the following to addComponent:
obj.addComponent(MyClass);
Or
obj.addComponent("MyClass");
There's no way to get the class using a name in javascript, it doesn't have something similar to the java ClassLoader.
You can get around that by creating your own mechanism, and there are probably many ways to do so, but here are 3 options.
(1) Maintain a registry for your component classes:
const REGISTRY: { [name: string]: ComponentType<Component> } = {};
class Component {}
class MyComponent1 extends Component {}
REGISTRY["MyComponent1"] = MyComponent1;
class MyComponent2 extends Component {}
REGISTRY["MyComponent2"] = MyComponent2;
type ComponentType<T extends Component> = {
new(): T;
}
function factory<T extends Component>(type: ComponentType<T> | string): T {
return typeof type === "string" ?
new REGISTRY[type]() as T:
new type();
}
(code in playground)
If you go with this approach then I suggest to make the REGISTRY an object that holds the collection, that way you can add the ctor only and get the name from that.
There's a variant for this and that's to use a decorator:
function register(constructor: typeof Component) {
REGISTRY[(constructor as any).name] = constructor;
}
#register
class MyComponent1 extends Component {}
#register
class MyComponent2 extends Component {}
(code in playground)
(2) Wrap the components in a namespace (As #Shilly suggested in a comment):
namespace components {
export class Component {}
export class MyComponent1 extends Component {}
export class MyComponent2 extends Component {}
export type ComponentType<T extends Component> = {
new(): T;
}
export function forName(name: string): ComponentType<Component> {
if (this[name] && this[name].prototype instanceof Component) {
return this[name];
}
}
}
function factory<T extends components.Component>(type: components.ComponentType<T> | string): T {
return typeof type === "string" ?
new (components.forName(type))() as T:
new type();
}
(code in playground)
If you're going with this approach then you need to make sure that all the component classes are exported.
(3) Use eval
class Component {}
class MyComponent1 extends Component {}
class MyComponent2 extends Component {}
type ComponentType<T extends Component> = {
new(): T;
}
function factory<T extends Component>(type: ComponentType<T> | string): T {
return typeof type === "string" ?
new (eval(type))() as T:
new type();
}
(code in playground)
This isn't a recommended approach, and you can read all about the cons in using eval in a lot of places.
But it's still an option so I'm listing it.
There is a way to instantiate classes by their name as String if they are in a namespace :
var variableName: any = new YourNamespace[YourClassNameString](ClassParameters);
For exmaple, this should work :
namespace myNamespace {
export class myClass {
example() {
return true;
}
}
}
var newClass: any = new myNamespace["myClass"](); // <- This loads the class A.
newClass.example();
This will instantiate the class myClass using the string "myClass".
Thus, to come back to your situation, I think this will work :
namespace myNamespace {
// The dependencies you defined
export class Component {
}
export interface ComponentType<T extends Component> {
new(): T;
}
// Just a class to contain the method's code
export class Example {
public addComponent<T extends Component>(type: ComponentType<T> | string): T {
let result: T;
if (typeof type === "string") {
result = new myNamespace[type]();
} else {
result = new type();
}
return result;
}
}
}
Then, you'll be able to do this :
let stringToLoad = "Component";
let classToLoad = Component;
let example = new Example();
let result1: Component = example.addComponent(stringToLoad);
let result2: Component = example.addComponent(classToLoad);
Playground version with code + test : here

Categories

Resources