For some reason we're having a ton of trouble using classes/prototypes in react native... we're not sure if we're doing something wrong, if es6 isn't actually supported or what. How can we use classes in react native? Clearly we aren't doing something right.
What we've tried
Creating a function and adding prototypes to it and exporting at the bottom
Creating and exporting a class with a constructor
Importing with {} and without, and exporting with default
The errors
Db is not a constructor
_Db2.default is not a constructor
Cannot read property 'default' of undefined
_Db.Db is not a constructor
No matter what we've tried, we cannot import an object of our creation and instantiate it. Here is an example of the prototype we've set up in another stackoverflow post we made when trying to untangle the issue
Here is an example of how we're importing it in.
import Db from '../localstorage/db/Db';
//var Db = require('../localstorage/db/Db');
const db = new Db();
When using require, it seems like the import statement works and an attribute we assign in the constructor exists, but none of the other prototypes are in the object.
EDIT 1: Below is our class implementation. We are instantiating realm outside of the class because realm seems to crash when instantiated inside of a class as documented in this github issue.
const realm = new Realm({
schema: [Wallet, WalletAddress, WalletTransaction, Log, APIWallet, APITransaction, APIAccount, Configuration],
path: config.db_path
});
export default class Db extends Object {
constructor() {
super();
this.realm = realm;
logger(2, realm.path);
}
//https://realm.io/docs/javascript/latest/#to-many-relationships
doOneToMany(one, many) {
many.forEach(m => {
this.write(() => {
one.push(m);
});
});
}
query(model, filter) {
let results = this.realm.objects(model);
if (filter) {
return results.filtered(filter);
}
return results;
}
insert(model, options) {
if (options == undefined && model instanceof Realm.Object) {
this.write(() => {
realm.create(model);
});
} else {
this.write(() => {
realm.create(model, options);
});
}
}
update(obj, options) {
this.write(() => {
Object.keys(options).map((key, attribute) => {
obj[key] = attribute;
});
});
}
del(model, obj) {
this.write(() => {
realm.delete(obj);
});
}
write(func) {
try {
realm.write(func);
} catch (e) {
logger(0, e);
throw new Error('Db.js :: Write operation failed ::', e);
}
}
close() {
Realm.close();
}
}
//module.exports = { Db };
The answer was we had a circular dependency in a Logger.js file that required Db.js while Db.js required Logger for useful logging. Removing the circular dependency caused classes and all the other import issues to go away.
Related
I'm working on a Node.js module in which several classes have to be exported. To save time, I decided to make export with a cycle. This is a snippet:
const _erros = {
MyError1: {
// fields
},
MyError2: {
// fields
}
// other errors
}
class BaseError extends Error {
constructor (data) {
// things
}
}
module.exports = Object.keys(_errors)
.reduce((acc, className) => Object.assign(acc, {
[className]: class extends BaseError {
constructor (message) {
const params = Object.assign({}, _errors[className])
params.message = message || params.message
super(params)
}
}
}), { BaseError })
Although the export is well done, VS Code IntelliSense detects only BaseError. Look at this image:
As you can see only BaseError is detected: MyError1, MyError2 and all other errors are not showed by IntelliSense.
Is this the proper VS Code behavior or it's a bug? And regardless the answer, is there a way to let VS Code IntelliSense work with exports made with cycles?
I'm mocking an interface class:
const error = "Child must implement method";
class MyInterface
{
normalFunction()
{
throw error;
}
async asyncFunction()
{
return new Promise(() => Promise.reject(error));
}
}
class MyImplementation extends MyInterface
{
}
If any of the interface methods are called without an overridden implementation, the error gets thrown. However these errors will only appear at time of execution.
Is there a way to check that the functions were overridden at construction?
You could add some inspection in the constructor of MyInterface, like this:
class MyInterface {
constructor() {
const proto = Object.getPrototypeOf(this);
const superProto = MyInterface.prototype;
const missing = Object.getOwnPropertyNames(superProto).find(name =>
typeof superProto[name] === "function" && !proto.hasOwnProperty(name)
);
if (missing) throw new TypeError(`${this.constructor.name} needs to implement ${missing}`);
}
normalFunction() {}
async asyncFunction() {}
}
class MyImplementation extends MyInterface {}
// Trigger the error:
new MyImplementation();
Note that there are still ways to create an instance of MyImplementation without running a constructor:
Object.create(MyImplementation.prototype)
Can't you use reflection in order to list all the function of a class?
For example here https://stackoverflow.com/a/31055217/10691359 give us a function which list all function of an object. Once you get all of them, you can see if you have or not an overridden function.
I have TypeScript code with classes of components. And I want to use somehow remote js file to extend this classes remote. So I want when my app starts to get js file remote and use code this for extend of needed class.
How to extend class I know. For example:
Import { UsersBlocksMyOrders } from "../pages/users/blocks/myorders";
declare module "../pages/users/blocks/myorders" {
interface UsersBlocksMyOrders {
logit(): void;
}
}
UsersBlocksMyOrders.prototype.logit = function () { console.log(this); }
In component file the code is:
import { APP_CONFIG } from "../../../app/app.config";
#Component({
selector: 'menu-blocks-menupage',
templateUrl: APP_CONFIG.appDomain + '/mobilesiteapp/template/?path=pages/menu/blocks/menupage'
})
export class MenuBlocksMenuPage{
constructor(){
this.logit();
}
}
My problem is that I use the Webpack to compile code. Webpack create final file where name of function is different. That's why I can't access to class directly.
How to be in this situation?
Create service to get file and extend class. You need to have variable inside class which is would store object where have keys. Keys it is names of classes to extend. And values with imported classes. Inside init function we are loop extending with prototype method.
import { Http } from "#angular/http";
import { MenuBlocksMenuPage } from "../pages/menu/blocks/menupage";
export class ExtendService {
allModules = {
...
MenuBlocksMenuPage : MenuBlocksMenuPage,
...
};
constructor(public http: Http){ }
init()
{
//Load json map to extend class
this.http.get(APP_CONFIG.appDomain + '/modules/mobilesiteapp/view/js/extend.json').toPromise()
.then((res) => {
let json = res.json();
//Loop each class to extend
Object.keys(json).forEach((cl) => {
//Add new functions and methods
Object.keys(json[cl]).forEach((func) => {
this.allModules[cl].prototype[func] = eval(json[cl][func]);
});
});
}).catch((e) => {
console.error(e);
});
}
}
Request json file with functions to eval.
{
"MenuBlocksMenuPage": {
"logit": "(function (){console.log(this);})"
}
}
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.
So I've got a fairly large sails.js application and I'm planning to migrate the codebase to TypeScript over the next few months, I modified sails moduleloader to use ts-node (I will probably open a pull-request very soon) and that's all fine.
The issue is that I'm not very satisfied with the TypeScript that I'm coming up with and wanted to know if anybody had any suggestions.
In sails a hook looks something like this:
module.exports = function(sails) {
return {
initialize: function(next) {
return next();
}
};
};
The TypeScript I initially came up with was
class Hook {
constructor(public sails: any) {}
initialize(next: Function) {
return next();
}
}
export = function(sails: any) {
return new Hook(sails);
}
But I don't find the export function great and there is the problem that sails bind the scope and therefore this will cause some problems. The solution is to use the fat arrow but I'm not a big fan of the solution I came up with...
class Hook {
constructor(public sails: any) {}
initialize = (next: Function) => {
return next();
};
}
export = function(sails: any) {
return new Hook(sails);
}
Any suggestions?
Thanks
I'm not sure what you're exactly looking for, but if you don't want to go fully OOP with a class Hook, you can just as well settle for an interface Hook. That way, you can keep your original code and just add some types to it.
interface Hook {
initialize(next: Function);
}
export = function(sails: any) : Hook {
return {
initialize: function(next) {
return next();
}
}
}