Update and Import Dynamic Constants - javascript

I have a file defining constants which have the current year as a part of them. In an effort to not update the code every year, I am making them dynamic. The constants can be updated by a dropdown list of years. When the user clicks on a new year, all constants should use that year.
How they are currently being used in a constants file being exported:
export const someConstants = {
CONSTANT_ONE_FOR_YEAR_2018: {
url: CONSTANT_BASE_URL + "more-info-2018",
otherInfo: {
SUPER_CONSTANT_VALUE: "1"
}
}
}
then later imported via
import { someConstants } from "../utils/constants";
console.log(someConstants.CONSTANT_ONE_FOR_YEAR_2018.url);
I was thinking there are a few ways, like changing someConstants to be a function accepting a date, but then that would require a date passed in every time.
export const someConstants = (year) => {
CONSTANT_ONE_FOR_YEAR_2018: {
url: CONSTANT_BASE_URL + `more-info-${year}`,
otherInfo: {
SUPER_CONSTANT_VALUE: "1"
}
}
}
or using a class:
export default class constants {
private _year: number = 2019;
private CONSTANT_BASE_URL: string = "https://";
set year(value: number) {
this._year = value;
}
constantsWithYear = () => {
return {
CONSTANT_ONE_FOR_YEAR_2018: {
url: this.CONSTANT_BASE_URL + `more-info-${this._year}`,
otherInfo: {
SUPER_CONSTANT_VALUE: "1"
}
}
}
}
But then to set and access it, you'd need to have the same instance of the class.
I realize technically constants shouldn't be dynamic, but how do I append a dynamic year to constants to be used throughout my project?

Constants are read-only, therefore you can not modify them later on.
you can use normal variable and make it like this:
`var someConstants = {
CONSTANT_ONE_FOR_YEAR_2018: {
url: CONSTANT_BASE_URL + "more-info-"+new Date().getFullYear(),
otherInfo: {
SUPER_CONSTANT_VALUE: "1"
}
}
}
`

Related

Circular reference using dependency inversion issue

I have a circular reference issue using this pattern approach. TypeError: Class extends value undefined is not a constructor or null .
The strange thing is, if I move the field.type.ts in src/constants.ts, it doesn't throw an error and it works as expected, but crashes on the Unit Tests. If it leave the fied.type.ts contents in it's own file, it crashes.
Maybe I am not using/understanding this dependency inversion pattern the right way. I could probably fixed by passing the FieldTypeToClassMapping as a parameter in Field.create(options: FieldOptions, fieldTypeMapping: FieldTypeToClassMapping), but I want to understand why this is happening.
import { StringField } from './string.field.model';
import { IntegerField } from './integer.field.model';
...
export const FieldTypeToClassMapping = {
//Constructor of eg. StringField class so I can use `new FieldTypeToClassMapping[options.type](options)`;
[FieldTypeEnum.STRING]: StringField,
[FieldTypeEnum.INTEGER]: IntegerField,
};
//field/field.ts
import { FieldOptions } from 'src/interfaces/field.options.interface';
import { FieldTypeToClassMapping } from 'src/model/template/field.type.to.mapping.ts'
export abstract class Field {
value: any;
type: string;
errors: string[] = [];
public constructor(options: FieldOptions) {
this.value = options.value;
this.type = options.type;
}
public static create(options: FieldOptions): any {
try {
return new FieldTypeToClassMapping[options.type](options);
} catch (e) {
throw new Error(`Invalid field type: ${options.type}`);
}
}
}
//field/integer.field.ts
import { FieldOptions } from 'src/interfaces/field.options.interface';
import { Field } from './field.model';
export class IntegerField extends Field {
constructor(options: FieldOptions) {
super(options);
}
protected validateValueDataType() {
this.validateDataType(this.value, "value");
}
protected validateDefaultDataType() {
this.validateDataType(this.defaultValue, "defaultValue");
}
}
//field/service.ts
payload const postFields = [
{
type: "string", //FieldTypeEnum.STRING,
value: 'a name'
},
];
const postFields = [
{
type: "string",
value: "John",
},
{
type: "integer",
value: 32,
},
];
const fieldsArray = [];
postFields.forEach((item) => {
const field: Field = Field.create(item);
fieldsArray.addField(field);
});
return fieldsArray;
The create(options: FieldOptions) function is defined inside the class Field, but then it tries to instantiate an instance of a class that extends Field.
I think that is where the problem arises. I don't know the entire contents of your files, but I imagine that at the top of any field.type.ts file you import Field. However since Field can instantiate any concrete implementation of itself it would need to know about them so you would need to import everything that extends Field inside Field.
I don't know/understand the dependency inversion pattern well enough to relate it to your question. But given the provided information, perhaps a Factory Pattern is what you need?
You could move the the function create(options: FieldOptions) to a FieldFactory class. Your create function is practically a factory function already.

Export javascript object to be used as new instance

So I have my menu.js file and inside I have a Menu object as shown below:
let Menu = {
elt: null,
settings: {
title: '',
}
}
export { Menu };
Now, I'm wanting to use the menu object to build another menu in another file, as shown here:
const test = new Menu('', {
// Empty
});
For some reason, it's not letting me use the export, I'm also tried export default Menu; with no luck, it keeps coming back with the following error:
Does anyone know the proper way to instantiate an object literal?
In ES6 (AKA ES2015), JavaScript introduced classes. These work just like classes in other programming languages (if you know any), with a constructor, methods, and getters and setters (all are optional). You might want to consider switching to the following code:
menu.js:
class Menu {
constructor() {
this.elt = null;
this.settings = {
title: ""
}
}
// Other methods
}
export { Menu }
main.js:
import { Menu } from "menu";
const test = new Menu();
However, if you want to put parameters, you can change the Menu constructor to:
constructor(elt = null, settings = {
title: ""
}) {
this.elt = elt;
this.settings = settings;
}
Then, in main.js,
import { Menu } from "menu";
const test = new Menu("", {
// Empty
});

Is there a way to destructure values inside a javascript object?

Lets say I have an object with constants
const FETCH_DATA = {
REQUESTED: 'FETCH_DATA_REQUESTED',
}
And now I want to use them in another object
const fetchData = {
requested() {
return {
type: FETCH_DATA.REQUESTED
};
},
}
But I don't want to retype the whole constant name each time.
Is it possible to do destructure? Something like this, only this doesn't work.
const fetchData = {
{REQUESTED}: FETCH_DATA,
requested() {
return {
type: REQUESTED
}
}
}
I can't put it outside the object due to REQUESTED being too general for the global scope as I have multiple REQUESTED constants.
You can create a module (= file) containing your constants and then export them as constants:
FetchDataContants.ts
export const REQUESTED = 'REQUESTED';
export const MYCONST = 'just an example';
then in another fileā€¦
import { REQUESTED, MYCONST } from './FetchDataConstants';
requested() {
return { type: REQUESTED }
}
or import all like so...
import * as c from './FetchDataConstants';
requested() {
return { type: c.REQUESTED }
}

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?

how do I make this class reuseable in typescript? meta programming? subclasses? injected module?

I've got this EventsStorage typescript class that is responsible for storing and retrieving Event objects in ionic-storage (wrapper for sqlite and indexedDB). It uses my Event class throughout.
I would like to reuse a lot of this logic for something other than an Event, like a Widget.
I come from a ruby background where it would be relatively simple to extract all the storage logic, set a ruby var that is literally the class Event and use that var wherever I use Event. Can I do something similar in typescript? Is there another mechanic I can use to reuse the bulk of this class for something else, like Widget?
Ideally, my EventsStorage class becomes really lightweight, and I'm not just wrapping calls to this.some_storage_module.get_ids() or this.some_storage_module.insert_new_objs() -- which would have to be copy/pasted to every other instance I needed this.
Something like this:
export class EventsStorage { // extends BaseStorage (maybe??)
constructor(){
super(Event, 'events'); // or some small set of magical args
}
}
Here's the existing class:
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { Event } from '../classes/event';
// EventsStorage < EntityStorage
// - tracks local storage info
// - a key to an array of saved objects
// - a query() method that returns saved objects
#Injectable()
export class EventsStorage {
base_key: string;
ids_key: string;
constructor(
private storage: Storage
){
this.base_key = 'event';
this.ids_key = [this.base_key, 'ids'].join('_');
}
get_ids(): Promise<any>{
return this.storage.ready().then(() => {
return this.storage.get(this.ids_key).then((val) => {
if(val === null){
return [];
} else {
return val;
}
});
});
}
insert_new_objs(new_objs: any): Promise<any>{
return new_objs.reduce((prev: Promise<string>, cur: any): Promise<any> => {
return prev.then(() => {
return this.storage.set(cur._id, cur.event);
});
}, Promise.resolve()).then(() => {
console.log('saving event_ids');
return this.storage.set(this.ids_key, new_objs.map(obj => obj._id));
});
}
update(events: Event[]): Promise<any> {
let new_objs = events.map((event) => {
return {
_id: [this.base_key, event.id].join('_'),
event: event
};
});
return this.insert_new_objs(new_objs);
}
query(): Promise<Event[]>{
let events = [];
return this.get_ids().then((ids) => {
return ids.reduce((prev: Promise<string>, cur: string): Promise<any> => {
return prev.then(() => {
return this.get_id(cur).then((raw_event) => {
events = events.concat([raw_event as Event]);
return events;
});
});
}, Promise.resolve());
});
}
get_id(id: string): Promise<Event>{
return this.storage.get(id).then((raw_event) => {
return raw_event;
});
}
}
It looks to me like you want to use generics. You basically define some basic interface between all the things you'll want to store, and your code should depend on that interface. In your code as far as I can tell you only use the id property.
So it would look kinda like this
import { Event } from '...';
import { Widget } from '...';
interface HasId{
id: string;
}
class ItemsStorage<T extends HasId> {
....
get_id(id: string): Promise<T>{
...
}
}
const EventStorage = new ItemsStorage<Events>(storage);
const WidgetStorage = new ItemsStorage<Widget>(storage);
const ev = EventStorage.get_id('abc'); //type is Promise<Event>
const wd = WidgetStorage.get_id('def'); //type is Promise<Widget>
You can read more about generics here.
Edit:
1 - about subclassing - It's usually less preferable. If your ItemsStorage class need different behavior when dealing with Events vs Widgets, than subclassing is your solution. But if you have the same behavior for every class, one might call your code generic, and using generics is better.

Categories