I started writing Ts, but now I'm a novice.
export class CrudController<AddDto extends CoreAddDto>{
protected readonly addDtotype: new () => AddDto;
constructor(addDtotype: (new () => AddDto)) {
this.addDtotype = addDtotype;
}
public async add(ctx: any, next: any) {
/// this undefined !!! ///
const dto = new this.addDtotype();
Object.assign(dto, ctx.request.body);
}
}
class FooController extends
CrudController<FooDto> {
constructor {
super(FooDto);
}
}
Why didn't I understand this command undefined?
this works
public add = async (ctx: any, next: any) => { }
method works as a property, why ??
This isn't a bug, your linter is correct because your add method is unbound. Basically there are two types of function declarations in JavaScript, regular function statements and arrow functions, and the primary difference is that regular function statements provide their own definition for this within their scope, while arrow functions binds to the current this in the scope they are defined.
So when a method is a normal function:
add(ctx: any, next: any) {
const dto = new this.addDtotype();
Object.assign(dto, ctx.request.body);
}
this points to add, and not to your CrudController but when you change it to an arrow:
public add = async (ctx: any, next: any) => { }
this is not overridden, and so it still points to your component. Note that you do not need to do this for built-in methods like render or componentDidMount, only for methods you define.
So basically anytime you want to use this in reference to your component while inside a method, you should define it like method = () => {}. If you seriously hate arrow functions, you can also bind methods in the constructor like so:
constructor(props){
super(props)
this.add = this.add.bind(this)
this.method = this.method.bind(this)
}
TSLint has a great rule to help prevent making this mistake (it's an easy one to make) called "no-unbound-method"
Related
Node: 17.7.1
Typescript: 4.6.3
I was working with an older repo on DDD and I came across a Typescript error as I was trying to recreate the code, which I am not understanding how to fix.
The IDE "error" occurs in AfterSomethingCreated class when registering with the code of :
Events.register(this.onSomethingCreatedEvent.bind(this), SomethingCreatedEvent.name);
Argument of type '(event: Event) => Promise<void>' is not assignable to parameter of type '(event: IEvent) => void'.
Types of parameters 'event' and 'event' are incompatible.
Property 'something' is missing in type 'IEvent' but required in type 'SomethingCreatedEvent'.ts(2345)
Class SomethingCreatedEvent implements IEvent interface. SomethingCreatedEvent also includes a property in addition to the properties from IEvent. When the property is included, the error is thrown, when taken out, the above error is thrown in the IDE
Code:
IEvent.ts
export interface IEvent {
//.....
}
IHandle.ts
export interface IHandle<IEvent> {
setupSubscriptions(): void;
}
Events.ts
export class Events {
//Methods...
public static register(callback: (event: IEvent) => void, eventClassName: string): void {
//Do Stuff
}
//Methods...
}
SomethingCreatedEvent.ts
export class SomethingCreatedEvent implements IEvent {
//.....
public something: Something;
constructor (something: Something) {
this.something = Something;
//.....
}
//......
}
}
AfterSomethingCreated (Where Error Is Occurring)
export class AfterSomethingCreated implements IHandle<SomethingCreatedEvent> {
constructor () {
this.setupSubscriptions();
}
setupSubscriptions(): void {
---> ERROR -> Events.register(this.onSomethingCreatedEvent.bind(this), SomethingCreatedEvent.name);
}
private async onSomethingCreatedEvent (event: SomethingCreatedEvent): Promise<void> {
//Do stuff
}
}
The error happens because Events.register() takes a callback that supposedly accepts any IEvent whatsoever. Thus it should be perfectly acceptable to actually call the callback with the minimal possible IEvent (in your case since IEvent is an empty interface this is just {}, the empty object):
public static register(callback: (event: IEvent) => void, eventClassName: string): void {
callback({}); // <-- look, no error
}
On the other hand the onSomethingCreatedEvent() method expects that its input will be a SomethingCreatedEvent, and so it should be perfectly acceptable for this method to access event properties unique to SomethingCreatedEvent objects, like the something property (whose value I am assuming is string, since you didn't define the Something type in your code. That is, I'm acting as if type Something = string;):
private async onSomethingCreatedEvent(event: SomethingCreatedEvent): Promise<void> {
console.log(event.something.toUpperCase());
}
But now inside setupSubscriptions() you are passing a callback which only accepts SomethingCreatedEvent events to Events.register(), which is an error:
setupSubscriptions(): void {
Events.register(this.onSomethingCreatedEvent.bind(this), SomethingCreatedEvent.name); // error!
// -----------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Argument of type '(event: SomethingCreatedEvent) => Promise<void>' is not
// assignable to parameter of type '(event: IEvent) => void'.
}
And that's an error for good reason. If you call the code as modified above, you get a runtime error because somewhere we're calling a callback with the wrong input:
new AfterSomethingCreated();
// RUNTIME ERROR: Uncaught (in promise) TypeError: event.something is undefined
Since this was existing code, presumably this doesn't actually happen in practice. There's actually a bunch of existing JavaScript which is technically unsafe this way. TypeScript checks method parameters in a bivariant way, meaning that it will allow both safe narrowing and unsafe widening operations. Function parameters are checked more strictly (assuming you have the --strictFunctionTypes compiler option enabled, which is part of the --strict suite of compiler features).
If you want to get the more loosely typed behavior, you need to represent the type of callback as a method instead of a function. By the way, here's the difference:
interface Test {
functionSyntax: (ev: IEvent) => void;
methodSyntax(ev: IEvent): void;
}
const test: Test = {
functionSyntax: (ev: SomethingCreatedEvent) => { }, // error!
methodSyntax: (ev: SomethingCreatedEvent) => { } // okay!
}
See how the declaration of functionSyntax in Test is a property with an arrow function expression type, while methodSyntax looks more like a method declaration. And see how the implementation of test complains about the functionSyntax property accepting too narrow of a type, while the methodSyntax property does not complain.
So if you want to just suppress the error, you can rely on method syntax. Well, it's a little tricky, because there's no method syntax for standalone functions. You can't write (ev: IEvent): void as a type, and {(ev: IEvent): void} is treated like function syntax. The trick here is to make an actual method type and then index into the surrounding object:
type MethodSyntax = { method(event: IEvent): void }["method"]
// type MethodSyntax = (event: IEvent) => void, but marked as a method
And now if you write Events.register() with that:
public static register(callback: MethodSyntax, eventClassName: string): void { }
Then your call will suddenly work with no error:
Events.register(this.onSomethingCreatedEvent.bind(this), SomethingCreatedEvent.name); // okay
This isn't any more type safe, but at least it's not in error.
If you care about enforcing type safety, then you'll probably need to refactor so that nothing bad can happen when a handler handles a callback. Here's one possible approach:
class Events {
static handlers: ((event: IEvent) => void)[] = [];
public static register<T extends IEvent>(
callback: (event: T) => void,
eventClass: new (...args: any) => T
): void {
this.handlers.push(ev => ev instanceof eventClass && callback(ev));
}
public static handleEvent(event: IEvent) {
this.handlers.forEach(h => h(event));
}
}
Now Events.register() is a generic function that accepts a callback that only accepts an event of type T, and an eventClass constructor (instead of a class name) for T. This way each handler can be called for each event... we don't call callback(event) unless event instanceof eventClass. With just a class name, it would be hard for the compiler to verify that any particular event would be appropriate for any particular callback, as the name property of classes is not strongly typed in TypeScript (see microsoft/TypeScript#43325 and issues linked within for more info).
And then the following is accepted now for SomethingCreatedEvent:
setupSubscriptions(): void {
Events.register(this.onSomethingCreatedEvent.bind(this), SomethingCreatedEvent);
}
while something inappropriate would be flagged:
Events.register((o: SomethingCreatedEvent) => { }, Date) // error!
// Property 'something' is missing in type 'Date' but required in type 'SomethingCreatedEvent'.
Playground link to code
This question already has answers here:
Declaring events in a TypeScript class which extends EventEmitter
(6 answers)
Closed 2 years ago.
I have a class in typescript, where I want to emit an event.
Now I know what I want is required for my code to work. But I would like this because it is nice to have.
My class extends the EventEmitter class, and I want to emit the event "check" (this works).
What I want is to have a list of possible events for intellisense, or the like, in Visual Studio Code.
How would I go about doing this in TypeScript?
There are two possible ways I know of:
The First One
Pretty simple, you can use npm packages like typed-event-emitter.
Code example from the package itself:
import { EventEmitter } from 'typed-event-emitter';
class MyClass extends EventEmitter {
onValueChanged = this.registerEvent<(newValue: number) => any>();
private _value: number;
constructor(value: number) {
// initialize EventEmitter
super();
this._value = value;
}
get value() {
return this._value;
}
set value(value: number) {
this._value = value;
this.emit(this.onValueChanged, this._value);
}
}
let instance = new MyClass(0);
instance.onValueChanged(newValue => {
console.log(`Value changed: ${newValue}`);
});
instance.value = 27;
The Second One
In case, you do not want to use npm packages and want to declare on your own, you can use declare interface on your classes.
Here is a short example of such declaration:
export declare interface Animal {
on: (event: 'tick', listener: () => void) => this
emit: (event: 'tick') => boolean
}
export class Animal extends EventEmitter {}
My service is designed in nodejs.
Below is my scenario
i have two controllers, one will be extending the other. there is a static function in both the controllers where in a static variable will be assigned some value.
depending on the condition of the data, im trying the make a call to the respective controller so that the static variable gets a appropriate assigned value.
Note:
The below code is just a snippet to explain the scenario and not the actual code of the application. But the order / calling / controller structure of this code snippet is exactly same. Also the listOfDept variable will be having separate business logic in the checkStart function of firstController and secondController.
// firstController.ts
firstController implements IFirstController {
private static listOfDept: string[];
static checkStart(){
firstController.listOfDept = // my logic to fill this object
}
constructor (){}
}
getRelevantData(next: (error: string, response: any) => void): void {
var myObject = firstController.listOfDept;
this.myRepository.uniqueData(myObject, next);
}
}
firstController.checkStart();
export = firstController;
//ifirstController.ts
interface IFirstController {
getRelevantData(next: (error: string, response: any) => void): void;
}
// secondController.ts
secondController extends firstController implements iSecondController {
private static listOfDept: string[];
static checkStart(){
firstController.listOfDept = ["Computer Science"];
}
constructor (){
super();
}
}
secondController.checkStart();
export = secondController;
//isecondController.ts
interface ISecondController implements ifirstController{}
//Controller calling the getRelevantData function
//middlewareController
middlewareController implements IMiddlewareController {
constructor(private firstController: IFirstController, private secondController: ISecondController) {
}
getDepData(data: any, next: (error: string, response: any) => void): void {
if(data.url = "fromParent") {
// im expecting this to make a call to checkStart() of firstController
this.firstController.getRelevantData();
} else {
// im expecting this to make a call to checkStart() of secondController
this.secondController.getRelevantData();
}
}
}
Problem faced with the above code
No matter which way the getRelevantData function is getting called, im always getting the value of listOfDept as computer science. It is never going in the checkStart function of first controller.
In general I would discourage using static methods for this kind of initialization and instead inject the required data into constructors or create factory methods for creating object with necessary data.
But, if you do want to use static properties, the problem is that you need to refer to the right parent class in the getRelevantData implementation. The class that constructed the instance can be accessed through constructor property. TypeScript does not process this scenario well, so you have to make a type cast:
// firstController.ts
class firstController implements IFirstController {
// Need to be `protected` to be accessible from subclass
protected static listOfDept: string[];
static checkStart(){
firstController.listOfDept; // my logic to fill this object
}
constructor (){}
getRelevantData(next: (error: string, response: any) => void): void {
// You need to refer to the constructor
let Class = this.constructor as typeof firstController;
var myObject = Class.listOfDept;
// the rest
}
}
firstController.checkStart();
//ifirstController.ts
interface IFirstController {
getRelevantData(next: (error: string, response: any) => void): void;
}
// secondController.ts
class secondController extends firstController implements ISecondController {
// No `listOfDept` definition here
static checkStart(){
secondController.listOfDept = ["Computer Science"];
}
constructor (){
super();
}
}
secondController.checkStart();
I am looking for a library agnostic way to "stack" functions. The paradigm's I am used to is "middleware", where something happens within a function errors can be thrown, and a context (or req) global is used to attach new properties or change existing ones. These ideas are found in libraries like express, or type-graphql.
I am looking for some agnostic way to chain middleware, not dependent on these type of libraries.
Here's an example of the kinds of functions I have.
I am struggling with some kind of clean way to author functions. The global approach is not complimentary to proper typing using typescript, and isn't very functional.
Where the more functional approach lacks this kind of "chainablity", where I can simply have an array of functions like below.
// logs the start of middleware
context.utility.log(debug, ids.onLoad),
// fetches user by email submitted
context.potentialUser.fetchByEmail(SignupOnSubmitArgs),
// throws error if the user is found
context.potentialUser.errorContextPropPresent,
// checks if passowrd and reenterPassword match
context.potentialUser.signupPassword(SignupOnSubmitArgs),
// creates the user
context.user.create(SignupOnSubmitArgs, ''),
// thows error if create failed in some way
context.user.errorContextPropAbsent,
// adds user id to session
context.utility.login,
// redirects user to dashboard
context.utility.redirect(Pages2.dashboardManage)
Is there any tools / libraries out there that will allow be to author clear and clean chain-able functions, and glue them together in a stackable way?
Returning this is usually the way for being able to chain methods. I made you an example showing both sync and async functions:
class ChainedOperations {
constructor(private value: number){}
public add(n: number): this {
this.value += n;
return this;
}
public subtract(n: number): this {
this.value -= n;
return this;
}
public async send(): Promise<this> {
console.log(`Sending ${this.value} somewhere`);
return this;
}
}
async function somewhereElse(): Promise<void> {
const firstChain = await new ChainedOperations(1).add(1).subtract(1).send();
await firstChain.add(1).subtract(2).send()
}
somewhereElse().catch(e => { throw new Error(e) });
For better dealing with async functions you can use pipe pattern where you chain but also wait for the final result and pass it to the next guy:
abstract class Pipable {
public pipe(...functions: Function[]) {
return (input: any) => functions.reduce((chain, func: any) => chain.then(func.bind(this)), Promise.resolve(input));
}
}
class AClass extends Pipable {
constructor(private value: number){
super();
}
public add(n: number): number {
this.value += n;
return this.value;
}
public subtract(n: number): number {
this.value -= n;
return this.value;
}
public async send(): Promise<number> {
console.log(`Sending ${this.value} somewhere`);
return this.value;
}
}
async function Something(){
const myClass = new AClass(2);
const composition = await myClass.pipe(myClass.add, myClass.subtract, myClass.send)(2);
}
Something();
Some people don't like to start from beginning but work their way backwards from the last function. If you want that just replace .reduce with .reduceRight. If you like fancy names, starting from last is called Composing as opposed to piping.
I have below code to add listeners on EventEmitter,
class MyClass {
init() {
this.listener = new EventEmitter();
this.listener.on('eventName', this.onChange.bind(this));
}
onChange() {
...
}
}
How can I remove the listener from EventEmitter? I can see two different ways:
this.listener.removeListener('eventName', this.onChange)
this.listener.removeListener('eventName', this.onChange.bind(this))
I wonder which one I should use. Whether I need the bind when I remove it?
You can use bind() or you can use an Arrow Function in Node. Arrow Functions will inherit their execution context from the invoking context, which is similar to how bind() works and provides the same functionality.
this.listener.removeListener('eventName', this.onChange)
The above way of removing a listener won't work if it is being called in a removeListener(eventName) style function like the following:
const obj = new MyObject()
obj.init()
// This would blow up with a
// ReferenceError saying you cant call removeListener() on undefined
obj.listener.removeListener('event')
Not sure the use of the init() function when you can use a constructor with the class syntax.
constructor() {
this.listener = new EventEmitter()
this.boundEventNameHandler = () => this.onChange()
this.listener.on('eventName', this.boundEventNameHandler)
}
You can utilize the bound function for removing a listener within the context of a class using this.
let boundEventNameHandler = this.boundEventNameHandler
this.listener.removeListener('eventName', this.boundEventNameHandler)`
An example of this implemented looks like the following
const EventEmitter = require('events')
class MyObject {
constructor () {
this.listener = new EventEmitter()
this.boundEventNameHandler = () => this.onChange()
this.listener.on('eventName', this.boundEventNameHandler)
}
// I would reconsider making this a property of the MyObject class
// instead, make onChange() a local function outside the MyObject
// class definition because onChange in this example is only used by the
// event listener for 'eventName'
onChange () {
console.log('event fired change')
}
removeListeners () {
let boundEventNameHandler = this.boundEventNameHandler
this.listener.removeListener('eventName', boundEventNameHandler)
}
}
For TypeScript / ESNext you can use this:
class MyClass {
constructor() {
this.emitter = new EventEmitter();
this.emitter.on("event1", this.listener1);
this.emitter.on("event2", this.listener2);
this.emitter.removeListener("event1", this.listener1);
this.emitter.removeListener("event2", this.listener2);
this.emitter.emit("event1"); // no output
this.emitter.emit("event2"); // no output
}
public listener1 = (e) => {
console.dir(`listener1`);
};
public listener2 = (e) => {
console.dir(`listener2`);
};
}
Basically you define properties and instantly assign bound functions to them.
This way you use the same function reference in .on and .removeListener.