I have class with property:
public requestLoadPersonal: Personal[] = [];
And one method:
private filterByGender(selectedValue: any): void {
console.log(this.requestLoadPersonal);
this.requestLoadPersonal = this.requestLoadPersonal.filter(
(p: Personal) => {
return selectedValue ? p.gender == selectedValue : true;
}
);
}
In constructor of class I have:
public filterFn = {
name: this.filterByGender
}
Why when I call function this.filterByGender from object filterFn by key. I got undefined message, why variable this.requestLoadPersonal is not available inside:
console.log(this.requestLoadPersonal); ?
Calling as:
this.filterFn['name']({...});
I tried to bind variable:
this.filterFn['name']({...}).bind(this.requestLoadPersonal);
In your constructor you should bind the function to this like this:
constructor() { this.filterFn = { name: this.filterByName.bind(this) }; }
Because just {name: this.filterByName} loses the this context, i.e.
filterByName has this context of some other instance.
However, I'd suggest you simplify everything and make it more straight-forward. Currently it's complex and thus, error-prone.
Related
I am working on custom class property decorator for my Mobx stores, that would automatically add a setter function to that property, like this:
class Hero() {
#setter
title = "knight";
}
const Ed = new Hero()
console.log(Ed.title) //"knight"
Ed.setTitle("Lord")
console.log(Ed.title) //"Lord"
I am working on legacy code that uses a lot of this decorator, but since it's relying on an outdated version of Mobx, I want to have my own decorator. I have a problem, however, to properly bind function call:
import { action } from 'mobx';
const setterName = name => {
return 'set' + name.charAt(0).toUpperCase() + name.slice(1);
};
export function setter(target, propertyName, desc, customName, customSetter) {
let name = customName || setterName(propertyName);
const setterFunction = function(value) {
if (!!customSetter) {
value =
typeof customSetter === 'function'
? customSetter.call(this, value)
: customSetter;
}
if (this[propertyName] !== value) {
this[propertyName] = value;
}
};
const fnDesc = {
value: action(setterFunction),
};
Object.defineProperty(target, name, fnDesc);
return desc && { ...desc, configurable: true };
}
This properly adds method to the object, but it's not bound and sometimes results in this being some component scope rather than always refer to the Class that decorator is placed in. When doing like this:
const fnDesc = {
value: action(setterFunction.bind(target)),
};
Object.defineProperty(target, name, fnDesc);
the scope is bound to the Class itself, not an actual instance. How can I change it so that it is being run in scope of the instance?
I have an empty javascript object, and I want to do something like this:
this.conflictDetails[this.instanceId][this.modelName][this.entityId] = { solved: true };
The problem is that for the modelName I get Cannot read properties of undefined.
I think it is because the empty object this.conflictDetails does not have the property that I m looking for, I wonder how I could write this to work and to be as clear as possible?
The result should look like:
{
'someInstanceId': {
'someModelName': {
'some entityId': {
solved: true;
}
}
}
}
With mu; time instance ids, that hold multiple model names, that have multiple entity Ids
check this the '?' operator
let obj = {
'someInstanceId': {
'someModelName': {
'someEntityId': {
solved: true
}
}
}
};
let value1 = obj.someInstanceId?.someModelName?.someEntityId;
console.log(value1);
// or
value1 = obj["someInstanceId"] ?? {};
value1 = value1["someModelName"] ?? {};
value1 = value1["someEntityId"];
console.log(value1);
let value2 = obj.someInstanceId?.someMissModelName?.someEntityId;
console.log(value2);
You can use interfaces to define the model of the object, or you can check if every nested object is not null nor undefined.
I would prefer the first option if I use typescript. If not, then i would stick to the not null/undefined option
First we create a model:
export interface CustomObject {
[instanceId: string]: {
[modelName: string]: {
[entityId: string]: {
solved: boolean;
};
};
};
}
Now that we have a model we can fill an object with data:
We define the object in the .ts file of your component
public dataObjects: CustomObject = {
instance1: {
basicModel: {
Entity1: {
solved: false,
},
},
}
};
Now you can have for example a function to change the solved status, I assume that there are several instanceIds, modelNames and entityIds.
public resolveObject(
instanceId: string,
modelName: string,
entity: string,
solved: boolean
) {
this.dataObjects[instanceId][modelName][entity].solved = solved;
}
Now you can call the method with the object details:
this.resolveObject('instance1', 'basicModel', 'Entity1', true);
After this line is executed the solved will changer from false to true.
I've created a StackBlitz to show you: https://stackblitz.com/edit/angular-ivy-ibfohk
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 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.
I am brand new to typescript, and I have two classes. In the parent class I have:
abstract class Component {
public deps: any = {};
public props: any = {};
public setProp(prop: string): any {
return <T>(val: T): T => {
this.props[prop] = val;
return val;
};
}
}
In the child class I have:
class Post extends Component {
public toggleBody: string;
constructor() {
this.toggleBody = this.setProp('showFullBody');
}
public showMore(): boolean {
return this.toggleBody(true);
}
public showLess(): boolean {
return this.toggleBody(false);
}
}
Both showMore and ShowLess give me the error, "Cannot invoke an expression whose type lacks a call signature."
But the function that setProp returns DOES have a call signature, I think? I think I'm misunderstanding something important about typings of functions, but I don't know what it is.
Thanks!
The function that it returns has a call signature, but you told Typescript to completely ignore that by adding : any in its signature.
"Cannot invoke an expression whose type lacks a call signature."
In your code :
class Post extends Component {
public toggleBody: string;
constructor() {
this.toggleBody = this.setProp('showFullBody');
}
public showMore(): boolean {
return this.toggleBody(true);
}
public showLess(): boolean {
return this.toggleBody(false);
}
}
You have public toggleBody: string;. You cannot call a string as a function. Hence errors on : this.toggleBody(true); and this.toggleBody(false);
Let's break this down:
The error says
Cannot invoke an expression whose type lacks a call signature.
The code:
The problem is in this line public toggleBody: string; &
it's relation to these lines:
...
return this.toggleBody(true);
...
return this.toggleBody(false);
The result:
Your saying toggleBody is a string but then your treating it like something that has a call signature (i.e. the structure of something that can be called: lambdas, proc, functions, methods, etc. In JS just function tho.). You need to change the declaration to be public toggleBody: (arg: boolean) => boolean;.
Extra Details:
"invoke" means your calling or applying a function.
"an expression" in Javascript is basically something that produces a value, so this.toggleBody() counts as an expression.
"type" is declared on this line public toggleBody: string
"lacks a call signature" this is because your trying to call something this.toggleBody() that doesn't have signature(i.e. the structure of something that can be called: lambdas, proc, functions, methods, etc.) that can be called. You said this.toggleBody is something that acts like a string.
In other words the error is saying
Cannot call an expression (this.toggleBody) because it's type (:string) lacks a call signature (bc it has a string signature.)
It means you're trying to call something that isn't a function
const foo = 'string'
foo() // error
I think what you want is:
abstract class Component {
public deps: any = {};
public props: any = {};
public makePropSetter<T>(prop: string): (val: T) => T {
return function(val) {
this.props[prop] = val
return val
}
}
}
class Post extends Component {
public toggleBody: (val: boolean) => boolean;
constructor () {
super()
this.toggleBody = this.makePropSetter<boolean>('showFullBody')
}
showMore (): boolean {
return this.toggleBody(true)
}
showLess (): boolean {
return this.toggleBody(false)
}
}
The important change is in setProp (i.e., makePropSetter in the new code). What you're really doing there is to say: this is a function, which provided with a property name, will return a function which allows you to change that property.
The <T> on makePropSetter allows you to lock that function in to a specific type. The <boolean> in the subclass's constructor is actually optional. Since you're assigning to toggleBody, and that already has the type fully specified, the TS compiler will be able to work it out on its own.
Then, in your subclass, you call that function, and the return type is now properly understood to be a function with a specific signature. Naturally, you'll need to have toggleBody respect that same signature.
This error can be caused when you are requesting a value from something and you put parenthesis at the end, as if it is a function call, yet the value is correctly retrieved without ending parenthesis. For example, if what you are accessing is a Property 'get' in Typescript.
private IMadeAMistakeHere(): void {
let mynumber = this.SuperCoolNumber();
}
private IDidItCorrectly(): void {
let mynumber = this.SuperCoolNumber;
}
private get SuperCoolNumber(): number {
let response = 42;
return response;
};
Add a type to your variable and then return.
Eg:
const myVariable : string [] = ['hello', 'there'];
const result = myVaraible.map(x=> {
return
{
x.id
}
});
=> Important part is adding the string[] type etc:
I had the same error message. In my case I had inadvertently mixed the ES6 export default function myFunc syntax with const myFunc = require('./myFunc');.
Using module.exports = myFunc; instead solved the issue.