Typescript: Property does not exist - javascript

I'm trying to develop a decorator for REST Api Interfaces in Typescript. Here it is the decorator implementation
export function RemoteResource(params: any): Function {
console.log("RemoteResource.params: ", params);
return function (target: Function) {
//--POST
target.prototype.post = function () {
console.log("----POST");
};
//--GET
target.prototype.retrieve = function () {
console.log("----GET");
};
//--DELETE
target.prototype.remove = function () {
console.log("----DELETE");
};
//--PULL
target.prototype.update = function () {
console.log("----PULL");
};
console.log("RemoteResource.target: ", target);
return target;
}
}
Now, I can use the decorator #RemoteResource and the methods post|retrieve|remove|update are added to the original object prototype correctly.
#RemoteResource({
path: "/foos",
methods: [],
requireAuth: false
})
export class Foo { }
From here, if I execute
let tester = new Foo();
tester.post() //--This prints out "----POST" correctly
I've the log printed out correctly, but I've also have the following error: "Property 'post' does not exist on type 'Foo'."
While I understand why I'm having this error (Foo doesn't have any declared post property) I'm not sure about how to fix it.
Ideally, I would like that the TS compiler understand that the decorator extends the original object adding up those methods.
How can I achieve it? Any ideas?
Thanks!

Since you are adding these methods dynamically at runtime in the decorator, the compiler has no way of knowing that these methods will exist for Foo instances.
You can change that in different ways, for example:
(1) Using an interface and intersection:
interface RemoteResource {
post(): void;
remove(): void;
update(): void;
retrieve(): void;
}
let tester = new Foo() as Foo & RemoteResource;
tester.post(); // no error
(2) Interface and empty methods:
export class Foo implements RemoteResource {
post: () => void;
remove: () => void;
update: () => void;
retrieve: () => void;
}
let tester = new Foo() as Foo & RemoteResource;
tester.post();
Edit
#Robba suggests:
(3) Ignore all type checking
let tester = new Foo() as any;
tester.post();
or
let tester = new Foo();
tester["post"]();

Related

How to avoid null/undefined check on JS/Typescript singleton with init function

I'm trying to find a pattern to avoid having to null/undefined check each literal props defined in an Object literal that is dynamically initiated only once at some point.
This object would serve as a singleton in a lifecycle of the application.
Is there a way to make this cleaner and avoid having to check and throw for each method that depends on the init to have happened? And also avoid ! operator on each prop once I know it is properly initiated?
I also want method2 to be able to be called before the init so that I can enqueue some things...
I guess I could switch my Object for a class and constructor (and define an instance as per singleton pattern) but the props for the constructor are needed from outside the class.
My method2 would also not be available until first constructed, unless I move it out of that class.
Any help appreciated.
interface StuffType {
x?: string;
y?: string;
z?: string;
fn?: () => void;
isInit: boolean;
}
const _stuff: StuffType = {
x: undefined,
y: undefined,
z: undefined,
fn: undefined,
isInit: false,
};
const isReady = () => _stuff.isInit;
const init = (x: string, y: string, z: string, fn: () => void) => {
if(_stuff.isInit) return;
_stuff.x = x;
_stuff.y = y;
_stuff.z = z;
_stuff.fn = fn;
};
const method1 = () => {
// depends on xyz, fn being defined
if (!_stuff.isInit) throw new Error('not init');
_stuff.fn!();
_stuff.x!;
_stuff.y!;
_stuff.z!;
};
// All the following methods would depend on init being done
// would require check on init/throw error/bang operator on props
// const method3 = () =>
// const method4 = () =>
// const method5 = () =>
// const method6 = () =>
const method2 = () => {
// does not depend on xyz, fn
};
export const Stuff = {
init,
isReady,
method1,
method2,
//etc...
};
Stuff.method2(); // fine
Stuff.method1(); // throws here
Stuff.init('x', 'y', 'z', () => console.log('init'));
Stuff.method1(); // does not throw
To do this more concisely, I'd put the data to be initialized in a single separate property - that way, you just need to check if that one property exists. This also makes the isInit property superfluous.
interface StuffType {
data?: {
x: string;
y: string;
z: string;
}
fn?: () => void;
}
const _stuff: StuffType = {
fn: undefined,
};
const init = (x: string, y: string, z: string, fn: () => void) => {
_stuff.data ??= { x, y, z };
};
const method1 = () => {
const { data } = _stuff;
if (!data) throw new Error('not init');
_stuff.fn!();
data.x;
data.y;
data.z;
};
I also want method2 to be able to be called before the init so that I can enqueue some things
Nothing in method2's signature shows that it depends on anything else, so no real modification there is necessary; you're free to populate
const method2 = () => {
// does not depend on xyz, fn
};
with whatever you want. (Perhaps you could have a queue variable local to the module, const queue = [], that gets pushed to if method2 is called)
I would avoid a partial type where every property is optional by itself. You can make them dependent on each other by using a discriminated union:
interface Stuff {
isInit: true;
x: string;
y: string;
z: string;
fn: () => void;
}
type MaybeStuff = Stuff | { isInit: false };
const _stuff: MaybeStuff = {
isInit: false,
};
export const method1 = () => {
if (!_stuff.isInit) throw new Error('not initialised');
// depends on x, y, z, fn being initialised
_stuff.fn();
_stuff.x;
_stuff.y;
_stuff.z;
};
However, an even simpler solution is to make _stuff a mutable variable that is initially undefined:
interface Stuff {
x: string;
y: string;
z: string;
fn: () => void;
}
let _stuff: Stuff | undefined; /*
^^^ */
export const isReady = () => _stuff !== undefined;
export function init(x: string, y: string, z: string, fn: () => void) {
if (_stuff) return; // throw new Error('already initialised');
stuff = {x, y, z, fn};
};
export function method1() {
if (!_stuff) throw new Error('not initialised');
// depends on _stuff being initialised
_stuff.fn();
_stuff.x;
_stuff.y;
_stuff.z;
}
export function method2() {
// does not depend on xyz, fn
}
If you do not like repeating the if (!_stuff) … line in all the methods that need the stuff, make a helper function for them:
function get(): Stuff {
if (!_stuff) throw new Error('not initialised');
return _stuff;
}
export function method1() {
const stuff = get(); // depends on _stuff being initialised
stuff.fn();
stuff.x;
stuff.y;
stuff.z;
}
export function method2() {
// does not depend on xyz, fn
}
export function method3() {
const stuff = get(); // depends on _stuff being initialised
…
}
[I have] an Object literal that is dynamically initiated only once at some point. This object would serve as a singleton in a lifecycle of the application. Is there a way to make this cleaner?
Yes, that doesn't sound a lot like a singleton, or at least like one with all of the common known singleton problems. A proper singleton should already be initialised at the start of the application, not at some point in its lifecycle, and it should not need parameters passed to it (exactly once). If it is parameterised, sooner or later you will need (multiple) different instances with different configuration.
So rather, refactor your module into a class, remove the mutable _stuff static (module-scoped) variable and the init method. Construct your object where the parameter values are available, and have that module be responsible for providing the instance to the rest of the application (via a global variable, the singleton pattern, dependency injection, etc.).
Also the method2 should either be a static method of the class if it is stateless, or it should be a method of the module keeping the initialisation state (if it does what you describe, enqueuing things until the initialisation).

implement interface that has anonymous call signatures

I am trying to create an instance of this interface
interface Logger {
(...args: any[]): void;
error(...args: any[]): void;
warn(...args: any[]): void;
info(...args: any[]): void;
verbose(...args: any[]): void;
}
i.e it has to be both callable and it has to be possible to access the properties.
const log: Logger = ...implementation //this is what I am looking for
log('asd') // this should work
log.error('foo') // this should work
How do I achieve this behaviour?
Solution is the same as for this question. Credit to #SamaraSoucy-MSFT
You implement it by creating a closure that invokes immediately. Define the main method and add properties to it. Example below.
const log = (function () {
let main = function () { console.log(...arguments); }
main.info = function () { console.log(...arguments); };
main.error = function () { console.log(...arguments); };
//...other methods
return main;
})();
log('asd')
log.info('this is info')
log.error('this is error')

Creating TypeScript class decorator for log correlation

I'm trying to create a TypeScript decorator that can be added to any class, that will create a request-scoped context that I can use to store a request ID. I've come across a couple of articles around decorators but none seem to fit my use-case.
Below is the code that I use to create the async hook, the decorator and a sample class that should be wrapped. When running the code, the actual response I receive is an empty object. The context is being lost but I'm not following why. I'm not receiving any errors or warnings.
Any suggestions would be highly appreciated.
This is the code I'm using to create the context. Calling the initContext() and getContext() functions.
import * as asyncHooks from 'async_hooks'
const contexts: any = {}
asyncHooks.createHook({
init: (asyncId: any, type: any, triggerAsyncId: any) => {
if (contexts[triggerAsyncId]) {
contexts[asyncId] = contexts[triggerAsyncId]
}
}
}).enable()
function initContext(fn: any) {
const asyncResource = new asyncHooks.AsyncResource('REQUEST_CONTEXT')
return asyncResource.runInAsyncScope(() => {
const asyncId = asyncHooks.executionAsyncId()
contexts[asyncId] = {}
return fn(contexts[asyncId])
})
}
function getContext() {
const asyncId = asyncHooks.executionAsyncId()
return contexts[asyncId] || {}
}
export { initContext, getContext }
This is the decorator that I'm using to wrap the class with. I'm trying to create the constructor within the context of the initContext function, set the context ID and then return the new constructor.
import { initContext } from './lib/context'
function AsyncHooksContext<T extends { new(...args: any[]): {} }>(target: T) {
return initContext((context: any) => {
context.id = 'some-uuid-goes-here'
return class extends target {
constructor(...args: any[]) {
super(...args)
}
}
})
}
export { AsyncHooksContext }
This is a sample class that should be able to produce the context ID
#AsyncHooksContext
class Foo {
public bar() {
const context = getContext()
console.log('This should be the context => ', { context })
}
}
new Foo().bar()

How to list Callbacks in a class

I was wondering if its possible to list callbacks in a class?
I already tried the answers listed here: Get functions (methods) of a class
But they do not list callbacks.
export default class Foo {
public myCallback: () => void;
private readonly bar: any;
constructor() {
console.log(Object.getOwnPropertyNames(this).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(this))​));
// [ 'bar', 'constructor', 'biz' ]
// 'myCallback' is not listed.
}
public biz() {
}
}
That is because of the way the javascript code is generated by the compiler.
Since the myCallback property is never set it optimizes the code and outputs nothing in javascript.
var Foo = (function() {
function Foo() {
// no my callback property is generated since it's never used
console.log(Object.getOwnPropertyNames(this).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(this))));
}
Foo.prototype.biz = function() {};
return Foo;
}());
However if at runtime you actually set that property it will be present. Like this.
class Foo {
public myCallback: () => void;
private readonly bar: any;
constructor() {
}
public logMethods() {
var props = [];
let obj = this;
do {
props = props.concat(Object.getOwnPropertyNames(obj));
} while (obj = Object.getPrototypeOf(obj));
props.forEach(method => console.log(method));
}
}
let a = new Foo();
a.myCallback = () => console.log('abc');
a.logMethods();
You can see the working example here.

How can I extend Backbone.Events in a Typescript ES6 Class?

I'm trying to attach Backbone's Events properties onto a TypeScript class, however when I do this...
class Foo {
constructor () {
_.assign(this, Backbone.Events); // or _.extend()
this.stopListening(this.otherInstance);
}
}
let bar = new Foo();
bar.on("myevent", handler);
...I get these compile time errors:
Error TS2339: Property 'stopListening' does not exist on type 'Foo'.
Error TS2339: Property 'on' does not exist on type 'Foo'.
I'm not very familiar with how TypeScript would approach this, but seems like something it could handle.
Note: looking for a solution that's easy to apply to multiple classes that also need Backbone.Events functionality (ie. I don't want to copy/paste all the on,off,listenTo... methods, or some funky proxy approach, to every class that needs them).
Since Backbone.Events is just an object, I can't extend it using normal ES6 syntax. Ex)
class Foo extends Backbone.Events {}
Ideas?
instead of _.assign if you use _.extend it will work,
Here is a Plunker
class Foo {
constructor () {
_.extend(this, Backbone.Events);
}
}
let bar : any = new Foo();
bar.on("alert", function(msg) {
alert("Triggered " + msg);
});
bar.trigger("alert", "an event");
updated code so that it does not gives compile time error.
UPDATE
you may create a class which has all the functions defined for Backbone.Events and constructor of it can extend Backbone.Events, which will override all the methods which is just defined for intellisense and type check.
updated the plunker
class CustomEvents {
constructor() {
_.extend(this, Backbone.Events);
}
on(eventName: string, callback?: Function, context?: any): any { return; };
off(eventName?: string, callback?: Function, context?: any): any { return; };
trigger(eventName: string, ...args: any[]): any { return; };
bind(eventName: string, callback: Function, context?: any): any { return; };
unbind(eventName?: string, callback?: Function, context?: any): any { return; };
once(events: string, callback: Function, context?: any): any { return; };
listenTo(object: any, events: string, callback: Function): any { return; };
listenToOnce(object: any, events: string, callback: Function): any { return; };
stopListening(object?: any, events?: string, callback?: Function): any { return; };
}
and you can extend any class with CustomEvents class like below,
class Foo extends CustomEvents {
constructor(){
super();
}
}
On Backbone.Events the event handling are attached on the object itself rather than on its .prototype, here is how you can correct that:
import {Events} from 'backbone';
interface IEventEmitter extends Events {
emit(event: string, ...args: any[]);
}
function _EventEmitter() {}
_EventEmitter.prototype = Events;
_EventEmitter.prototype.emit = (Events as any).trigger;
export const EventEmitter: new() => IEventEmitter
= _EventEmitter as any as new() => IEventEmitter;
Now use it as by inheritance:
class Dog extends EventEmitter {
}
var dog = new Dog;
dog.on('bark', () => console.log('Dog just barked'));
dog.emit('bark');
Use typings from here - https://www.npmjs.com/package/#types/backbone
Write implementation of Backbone.EventsMixin somewhere right after Backbone script is included:
Backbone.EventsMixin = function () {
_.assign(this, Backbone.Events);
}
Then it can be used like this:
class SomeClass extends Backbone.EventsMixin

Categories

Resources