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();
}
}
}
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 a new developper in Node.js (coming from Python), and I'm quite surprised to see that there are no decorators in Javascript. I would like to use it, however, because it greatly simplifies the code.
After some research, I found an ES6 specification in stage 2 for this (https://github.com/tc39/proposal-decorators), but it is apparently not supported in Node.js. I also found this in TypeScript, but their function is limited (only in classes).
So my question is: Is there a way to have decorators similar to those of Python with Node.js, and if not, what features can be used as a substitute ?
Thank you in advance for your answers !
According to Wikipedia decorators in python are just syntactic sugar for a function call, which takes the original class/function and stores the returned value under the variable containing the class. Thus the equivalent js would be:
const Decorated = decorator(class ToBeDecorated {
/*...*/
});
const decoratedFunction = decorateFunction(function decorated() {
/*...*/
});
// Some sample decorator implementations:
const decorator = Parent => class WithDuck extends Parent {
quack() { }
};
const decorateFunction = fn => (...args) => {
console.log(`${fn.name} called with`, ...args);
return fn(...args);
};
#jonas Wilms 's answer is correct. I wanted to post this here to expand on a specific implementation technique I used. My use case was with Koa, using async/await functions.
First, I created my decorator:
export function Cocoon( { param=[] }, callback )
{
//Return a function from the decorator
return async (ctx) => {
//Do a thing with Param
const params = { usr: param }
return await callback( ctx, params )
}
}
Using the decorator to annotate a function:
export const list = Cocoon({
param: [5]
},
async (ctx, { usr }) => {
//Function body
ctx.response.body = usr
}
)
Now I can export 'list' to Koa and it'll be called as expected.
I'm trying to extend a 3rd party class but am having trouble getting typescript to play nice. Basically, I can't use any existing method already defined in the class in my new method.
A workaround would be to redefine existing methods in extensions.ts (see below), but there just has to be a better way.
3rd party index.d.ts
export as namespace thirdParty;
export Class SomeClass {
// some methods here
}
My extensions.ts
import {thirdParty} from 'thirdParty'
declare module 'thirdParty' {
namespace thirdParty {
class SomeClass{
newMethod(): this
// works if I redfine the method here
originalExistingMethod(): number
}
}
}
thirdParty.SomeClass.prototype.newMethod = function() {
return this.originalExistingMethod() + 1
}
When calling an existing method like this.originalExistingMethod() above, typescript complains:
TS2339: Property 'originalExistingMethod' does not exist on type 'SomeClass'
Is there a way to avoid having to redefine existing methods when performing module augmentation?
Initial Answer
Here is an example using the Tensorflow library.
extend.ts
import { AdadeltaOptimizer } from '#tensorflow/tfjs-core';
declare module '#tensorflow/tfjs-core' {
interface AdadeltaOptimizer {
newMethod(message: string): void;
}
}
AdadeltaOptimizer.prototype.newMethod = function (message: string) {
console.log('===============');
console.log(message);
console.log('===============');
}
index.ts
import { AdadeltaOptimizer } from '#tensorflow/tfjs';
import "./extend";
const optimizer = new AdadeltaOptimizer(10, 10);
// the existing method is present
const className = optimizer.getClassName();
// the augmentation is also present
optimizer.newMethod(`The className is ${className}.`);
There is a similar example in the official TypeScript documentation, which augments Observable with a map method.
Follow Up on Comments
Thanks. Though my issue is using existing methods when defining newMethod. So in extend.ts not in index.ts. Any ideas on this?
This also works in extend.ts as follows:
import { AdadeltaOptimizer } from '#tensorflow/tfjs-core';
declare module '#tensorflow/tfjs-core' {
interface AdadeltaOptimizer {
newMethod(message: string): void;
}
}
AdadeltaOptimizer.prototype.newMethod = function (message: string) {
// just access the original method on `this`
const className = this.getClassName();
console.log('===============');
console.log(className);
console.log(message);
console.log('===============');
}
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.
Am trying to make an abstract class service with methods and properties that other services can inherit n Angularjs. Using typescripts extend method would not work or i don't if am doing it properly, so tried using this pattern but sadly it too did not work. Typescript does not know the inherited methods from the prototypal inheritance. Is their any workaround or a solution to this, thank you for the help
example code for the Abstract service Class
'use strict';
namespace Data {
export interface IAbstractRepository {
sayHellow(): string;
}
class AbstractRepository implements IAbstractRepository{
static extend = function (repoCtor): void {
repoCtor.prototype = new AbstractRepository();
repoCtor.prototype.constructor = repoCtor;
};
sayHellow(): string {
return 'Hello world';
}
}
function factory(): IAbstractRepository {
return AbstractRepository;
}
angular.module('core').factory('AbstractRepository', factory);
}
and for the sub service class
'use strict';
namespace Data{
class BookRepository {
constructor(AbstractRepository) {
AbstractRepository.extend(this);
}
getAllBooks(): string {
// this shows as an error now it cant know that its inherited
return this.sayHellow();
}
}
factory.$inject = ['AbstractRepository'];
function factory(AbstractRepository): any {
return new BookRepository(AbstractRepository);
}
angular.module('core').factory('BookRepository', factory);
}
for the solution proposed down the flags for JshintRC to suppress warnings produced by Typescript
"validthis": true and "shadow": "false
Your question is not that clear, even with the comment you answered my question I'm still not sure what the problem is.
I'm not angular developer, so I can't answer angular specific questions, but as inheritance goes in typescript this is how you do it:
namespace Data {
export interface IAbstractRepository {
sayHellow(): string;
}
abstract class AbstractRepository implements IAbstractRepository {
constructor() {}
sayHellow(): string {
return 'Hello world';
}
}
class BookRepository extends AbstractRepository {
constructor() {
super();
}
getAllBooks(): string {
return this.sayHellow();
}
}
angular.module("core").factory("BookRepository", BookRepository);
}
If you'll tell me what's wrong with this, then I might be able to help you solve that.
Edit
Since the playground url is too long for the comments, here it is.