Anyone know why this doesn't work and how to fix? I am trying to say that the onChange function takes a key from the generic State as it's argument. But it's not working the way I thought it would.
function test<State>() {
const onChange = (key: $Keys<State>) => {}
return onChange;
}
const INITIAL = { foo: 'bar' };
function hey() {
const change = test<typeof INITIAL>();
// Error here
// Cannot call `change` with `'foo'` bound to `key` because property `foo` is missing in `State`
change('foo');
}
https://flow.org/try/#0GYVwdgxgLglg9mABFApgZygHgMpQIaoB8AFAJSIDeAUAJAQIaIIDCAFnmAOYqIC8ixANYoAngC5EAEgDSotDnxFyvQpQC+VRFsQAnFFBA6kLdlxQBuKhqr0wjAJIA5ewBV7AQQAyfSomBw4CQByACM8HSDENUsqUEhYBERWUTJKTToGKEQIU24fVAxMKBEABxQ4YEQnVw9PElJLLTpclGIg-zgghqsgA
Typing the return type of the function fixes it.
function test<State: {}>(): (key: $Keys<State>) => void {
const onChange = (key) => {}
return onChange;
}
const INITIAL = { foo: 'bar' };
function hey() {
const change = test<typeof INITIAL>();
change('foo');
}
https://flow.org/try/#0GYVwdgxgLglg9mABFApgZygHgMpQIaoBciA3gL4B8AFAJTFUDWKAnsQCQDSLaO+qFNRAF4KiAG5wYAE1IAoAJAQEGRAgDCACzxgA5imGJGLQSNJlZiS4gBOKKCGtJ1W3SgDcs80rAqAkgDlfABVfAEEAGQMSRGA4OGIAcgAjPGsExDIPUEhYBEQNFlo5REVlKEQIFz0DVAxMKGYABxQ4YEQA4LDw6hoPS0UqlCoE2LgE3s8gA
Related
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
I created a basic emitter and receiver. Please can you let me know why when I console log the receivers messages it returns an empty array?
class Emitter {
constructor(messages = []) {
this.messages = messages;
this.event = () => {};
}
setEvent(fn) {
this.event = fn;
}
trigger() {
this.messages.forEach(message => this.event(message));
}
}
class Reciever {
constructor() {
this.messages = []
}
ping(message) {
console.log(message)
this.messages.push(message)
}
}
const myReciever = new Reciever();
const myEmitter = new Emitter(message = ["A", "B", "C"]);
myEmitter.setEvent(myReciever.ping);
myEmitter.trigger();
console.log(myReciever.messages);
From the comment above ...
"At the time of setEvent the code doesn't care about the this context of myReciever.ping. ... ping gets assigned to an emitters own event just as function, immediately getting oblivious where it originally did belong to."
Besides the already suggested solution of actively binding thisArg to its method, one also can adapt the code in a way that one can or has to pass to setEvent a methods's target/context alongside the method/function itself. At trigger time the method then will be called within this stored context ...
class Emitter {
constructor(messages = []) {
this.messages = messages;
this.handler = () => {};
this.target = null;
}
setEvent(handler, target) {
// `setEvent` might be better renamed to
// `setHandler` or `assignHandler`, etc.
this.handler = handler ?? (() => {});
this.target = target ?? null;
}
trigger() {
this.messages.forEach(message =>
this.handler.call(this.target, message)
);
}
}
class Reciever {
constructor() {
this.messages = []
}
ping(message) {
console.log(message)
this.messages.push(message)
}
}
const myReciever = new Reciever();
const myEmitter = new Emitter(message = ["A", "B", "C"]);
myEmitter.setEvent(myReciever.ping, myReciever);
myEmitter.trigger();
console.log(myReciever.messages);
You are losing context on the ping call:
myEmitter.setEvent(myReciever.ping);
You need to bind it.
myEmitter.setEvent(myReciever.ping.bind(myReciever));
You are storing everything in the emitter, not the receiver. If you print the emitter, the entries are duplicated.
This is because when you pass in the function to the .setEvent(...) you are using this which - in this scenario - is referring to the messages from the emitter; it lost the context of the object it belongs to.
Like #MinusFour indicated, you need to bind the function instead.
See demo below
class Emitter {
constructor(messages = []) {
this.messages = messages;
this.event = () => {};
}
setEvent(fn) {
this.event = fn;
}
trigger() {
this.messages.forEach(message => this.event(message));
}
}
class Reciever {
constructor() {
this.messages = []
}
ping(message) {
console.log(message)
this.messages.push(message)
}
}
const myReciever = new Reciever();
const myEmitter = new Emitter(message = ["A", "B", "C"]);
myEmitter.setEvent(myReciever.ping.bind(myReciever));
myEmitter.trigger();
console.log(myReciever.messages);
I am reviewing some Javascript code and stumbled upon a syntax that I didn't knew. The application is a React and Redux one, though I think this is plain Javascript.
The syntax I'm concerned with is the { f1(), f2(), ... } argument of combineReducers().
This is the syntax:
combineReducers({
Reducer1,
Reducer2,
...
});
ReducerN is a function, i.e.:
const Reducer1 = (state = INITIAL_STATE, action) => {
// ...
};
I get { f1(), ... } creates an object where the function name is the key and the function itself is the value, so in a browser console I tried the following:
a = () => { console.log(1) }
b = () => { console.log(2) }
o = {a, b}
and if I print o:
{a: ƒ, b: ƒ}
a: () => { console.log(1) }
b: () => { console.log(2) }
__proto__: Object
But if I try to initialize o in a single operation:
o = { () => return 1 }
or
o = { function y() { return 1 }}
they both give a syntax error.
It's the first time I see an object created with that syntax: What kind is that? Where can I find its reference?
As said previously,
combineReducers({
Reducer1,
Reducer2,
...
});
is equivalent to this in plain ES5:
combineReducers({
Reducer1: Reducer1,
Reducer2: Reducer2,
...
});
and combineReducers is concerned only with the values of the object passed in. The first form is just a shorthand for defining properties with the same name as the value. This is the reason you cannot use anonymous functions in this form. To define function members on classes and objects, you can use the following form:
class Foo {
foo() { console.log('foo'); }
bar = () => console.log('bar')
}
const a = new Foo();
a.foo();
a.bar();
const b = {
foo() { console.log('foo'); }
bar: () => console.log('bar')
};
b.foo();
b.bar();
When transpiling to plain ES5, this will generate the following:
"use strict";
var Foo = /** #class */ (function () {
function Foo() {
this.bar = function () { return console.log('bar'); };
}
Foo.prototype.foo = function () { console.log('foo'); };
return Foo;
}());
var a = new Foo();
a.foo();
a.bar();
var b = {
foo: function () { console.log('foo'); },
bar: function () { return console.log('bar'); }
};
b.foo();
b.bar();
{ f1() } is very different than { f1 }.
The latter is a shorthand of { f1: f1 } which is an object having the key 'f1' (a string) associated to the value f1 (a function). The function is not executed.
In the first example f1() is a function call. The function f1 is executed and the value it returns is used instead. But because you didn't provide a key to associate the value with and because f1() is a value that does not have a name (it is an expression that needs to be evaluated in order to get its value), JS cannot produce an object out of it.
{ f1 } can be evaluated at the compile time and turned into { f1: f1 }.
{ f1() } cannot be evaluated at the compile time. The value of f1() is available only at the run time.
This is why { f1() } is invalid code.
If you need to call f1 and use the value it returns to create an object you can do it this way:
const x = { f1: f1() }
This is the same thing as:
const v = f1();
const x = { f1: v }
Is it possible to use destructuring in an interface where a property depends on other one?
My arguments are the following:
interface PropsVariationA {
functionToExecute?: () => void;
executeExtraFunction: true;
}
interface PropsVariationB {
executeExtraFunction: false;
}
type Props = PropsVariationA | PropsVariationB;
So when I try to use it with destructuring I (obviously) get an error:
function SomeFunctionWithDestructuring({
executeExtraFunction,
functionToExecute = () => {} // Error: property 'functionToExecute' doesnt exist on type 'Props'
}: Props) {
if (executeExtraFunction) functionToExecute();
return null;
}
Check out this demo.
I managed to make it work without destructuring but I wonder if there is any way.
If you set functionToExecute to undefined in PropsVariationB then it would work:
interface PropsVariationA {
functionToExecute?: () => void;
executeExtraFunction: true;
}
interface PropsVariationB {
functionToExecute: undefined;
executeExtraFunction: false;
}
type Props = PropsVariationA | PropsVariationB;
// function without destructuring
function SomeFunction(props: Props) {
if (props.executeExtraFunction && props.functionToExecute)
props.functionToExecute();
return "a";
}
// function with destructuring
function SomeFunctionWithDestructuring({
executeExtraFunction,
functionToExecute = () => {} // Any way to do destructuing here?
}: Props) {
if (executeExtraFunction) functionToExecute();
return null;
}
// This error is right
SomeFunction({ executeExtraFunction: false, functionToExecute: () => {} });
I'm trying to create a class which allows passing a callback to alter the side-effects of a method. If you don't pass a callback, then the method will be called directly. This is a basic example:
class Button<T = void> {
private clickWrapper?: (click: Function) => T
private _click() {
// do the click here
return null;
}
constructor(clickWrapper?: (click: Function) => T) {
this.clickWrapper = clickWrapper;
}
public click() {
if (this.clickWrapper) {
return this.clickWrapper(this._click.bind(this));
} else {
return this._click();
}
}
}
class Foo {
public doStuff() {
console.log('hello');
}
}
const button = new Button<Foo>(click => {
// do some stuff
click();
return new Foo();
});
const foo = button.click();
foo.doStuff();
const button2 = new Button();
button2.click();
This works, but foo.doStuff() complains that foo may be null - even though in this case I provided a clickWrapper, so the return value of button.click() cannot be null, it must be an instance of Foo. Is there a better way to define this?
The second issue is I have to copy the Button constructor's parameter type when I've already declared it for Button.clickWrapper. How do I avoid having to declare the type on the private property and constructor parameter?
I have updated you code snippet:
class Button<T = null> {
constructor(private clickWrapper?: (click: Function) => T) {}
private _click() {
// do the click here
return null;
}
public click(): T {
if (this.clickWrapper) {
return this.clickWrapper(this._click.bind(this));
} else {
return this._click();
}
}
}
class Foo {
public doStuff() {
console.log("hello");
}
}
const button = new Button<Foo>(click => {
// do some stuff
click();
return new Foo();
});
const foo = button.click();
foo.doStuff();
const button2 = new Button();
button2.click();
Two things:
TypeScript can't be sure what is exact return type of your public click function so it assumes T | null, since default _click function returns null
To avoid redeclaring types for constructor and property of an object, you can always use shorthand syntax for constructor assignment (just add private or public keyword to constructor param)
interface Callback<V> {
(arg: () => void): V
}
class Button<T = void> {
constructor(private callback?: Callback<T>) {}
private onClick = () => {
}
public click = () => {
if (this.callback) {
return this.callback(this.onClick)
} else {
return this.onClick()
}
}
}
const button = new Button<number>(
click => {
click()
return 2 +2
}
)
console.log(button.click()) // 4
I update your code to solve your problems
Create an interface for the callback type and add the private callback? to the constructor to inject the argument to the class
There are many types for a function, in typescript a function that not return nothing is a void function, you are returning null, so that didn't match with your clickWrapper type, I assume you aren't gonna return anything from the click function so I update that type to match too with a void function
I have read "How to implement a typescript decorator?" and multiple sources but there is something that i have nor been able to do with decorators.
class FooBar {
public foo(arg): void {
console.log(this);
this.bar(arg);
}
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
If we invoke the function foo:
var foobar = new FooBar();
foobar.foo("test");
The object FooBar is logged in the console by console.log(this); in foo
The string "FooBar {foo: function, bar: function} bar test" is logged in the console by console.log(this, "bar", arg); in bar.
Now let's use a decorator:
function log(target: Function, key: string, value: any) {
return {
value: (...args: any[]) => {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args); // How to avoid hard coded this?
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
We use the same function but decorated:
class FooBar {
#log
public foo(arg): void {
console.log(this);
this.bar(arg);
}
#log
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
And we invoke foo as we did before:
var foobarFoo = new FooBar();
foobarFooBar.foo("test");
The objectWindow is logged in the console by console.log(this); in foo
And bar is never invoked by foo because this.bar(arg); causes Uncaught TypeError: this.bar is not a function.
The problem is the hardcoded this inside the log decorator:
value.value.apply(this, args);
How can I conserve the original this value?
Don't use an arrow function. Use a function expression:
function log(target: Object, key: string, value: any) {
return {
value: function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
That way it will use the function's this context instead of the value of this when log is called.
By the way, I would recommend editing the descriptor/value parameter and return that instead of overwriting it by returning a new descriptor. That way you keep the properties currently in the descriptor and won't overwrite what another decorator might have done to the descriptor:
function log(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
var originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = originalMethod.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
};
return descriptor;
}
More details in this answer - See the "Bad vs Good" example under "Example - Without Arguments > Notes"
I believe you can use
var self = this;
in order to preserve the 'this' at that specific point. Then, just use self at the later point where you would have wanted that particular this