Where to put a method without "this"? - javascript

My code:
class DoIt {
city () {
// others operations...
const number = 5; // from operations
const amount = this.getAmount (number);
// others operations...
}
street () {
// others operations...
const number = 10; // from operations
const amount = this.getAmount (number);
// others operations...
}
getAmount (number) {
return number * 10 * 3.14 / 3; // example...
}
}
I use "eslint" to check my code and I have error:
error Expected 'this' to be used by class method 'getAmount'
class-methods-use-this
So where to put a method without "this"? This code is important for my class, so I shouldn't create helper class...
Maybe this method is not important? In OOP in other languages can exists method without 'this' in class methods?

class-methods-use-this is an eslint check to see if your class method does not refer to this. In some cases, it may be better to make it static by adding the static keyword in front of the method declaration. In other cases, you can just ignore this check.
https://eslint.org/docs/rules/class-methods-use-this

Related

How to grab JavaScript destructured parameters independent of variable name?

Wish to enact upon arguments but with defaults defined
In my quest for self-documenting code with destructuring but being DRY wanting to do this...
async function shampoo({ lather = true, rinse = true, repeat = 2 } = {}) {
await api.dogWasherMachine(magicalArguments) // ???
// don't want to have to do this:
// api.dogWasherMachine({ lather, rinse, repeat })
// currenty arguments is {} and actual input is defined
}
How do I get these magical arguments that are defined?
arguments do not have the default input defined but how can I do this?
It's not possible to do it in the parameters alone - destructuring necessarily extracts each property into an independent named variable, without leaving a reference to the original object behind. You'll have to do it with another statement, eg:
async function shampoo(param = {}) {
const defaultObj = {
lather: true,
rinse: true,
repeat: 2
};
await api.dogWasherMachine({ ...defaultObj, ...param });
}
I use destructuring assignment to to self document inheritable classes that are used as interfaces. On construction I get all of the benefits of intellisense and when calling an API everything stays nice and DRY.
class washable {
constructor({ lather = true, rinse = true, repeat = 2 } = {}) {
this.lather = lather
this.rinse = rinse
this.repeat = repeat
}
}
class dog extends washable {
async shampoo() {
await api.dogWasherMachine(this)
}
}

ES6 pass function as parameter example

I don't know JS/ES6 well enough to describe my question in code. So most of this question is conceptually and in pseudo code.
Say I have a Contractor class like this:
class Contractor {
constructor(jobFn) {
// save jobFn;
}
dailyRoutine() {
// let result = DriveToWork()
const result = 6
DoTheJob(result)
DriveBackHome()
}
}
The problem is, what the DoTheJob() does might be different things in different places.
So in place A, it could be
he = new Contractor(write_front_end(with, this, and that))
And in place B, it could be
he = new Contractor(fix_backend_node(with, express))
I.e., the behavior need to be passed in during the constructor, and the action might need to take different kind and different amount of parameters.
Would such thing be possible with ES6?
Please show ES6 code that can pass function with different kind and different amount of parameters through the constructor to DoTheJob().
Further, the challenge is that the jobFn need to be a Curried function, meaning there is one or more parameter missing to do the DoTheJob job. Say if the jobFn is passed with Curried add(3), then DoTheJob will do UncurriedAdd of add(3, 6); if then jobFn is passed with Curried multiple(5), then DoTheJob will do Uncurried of multiple(5, 6);
Just assign the passed function to this.DoTheJob, and then call this.DoTheJob inside dailyRoutine:
class Contractor {
constructor(jobFn) {
this.DoTheJob = jobFn;
}
dailyRoutine() {
// DriveToWork()
this.DoTheJob();
// DriveBackHome()
}
}
const c1 = new Contractor(() => console.log('doing job A'));
c1.dailyRoutine();
const c2 = new Contractor(() => console.log('doing job B'));
c2.dailyRoutine();
// c1 again:
c1.dailyRoutine();
// feel free to reference any in-scope variables in the passed function,
// no need to pass the variables as additional parameters
const data = 'data';
const c3 = new Contractor(() => console.log('data is', data));
c3.dailyRoutine();
If dailyRoutine needs to be invoked with data that needs to be sent to the passed doTheJob function, just define the needed arguments in the function you pass, there's no need for actual currying here:
class Contractor {
constructor(jobFn) {
this.DoTheJob = jobFn;
}
dailyRoutine(doJobArg) {
this.DoTheJob(doJobArg);
}
}
// feel free to reference any in-scope variables in the passed function,
// no need to pass the variables as additional parameters
const data = 'data';
const c3 = new Contractor((arg) => console.log('data is', data, 'and arg is', arg));
c3.dailyRoutine('argDoTheJobIsCalledWith');
In my case, I may advise you that it's better to give the predicate to dailyRoutine, because this way you'll be able to reuse the same instance and give different predicates.
Anyway, there's a pure OOP solution for this, using method polymorphism, the JavaScript way (aka duck typing):
class Contractor {
driveBackHome() {}
dailyRoutine() {
const result = 6
this.doTheJob(result)
this.driveBackHome()
}
}
class SpecializedContractorA extends Contractor {
doTheJob(result) {
console.log('aaaaaaaaaaaaaaaaaaaaa', result)
}
}
class SpecializedContractorB extends Contractor {
doTheJob(result) {
console.log('bbbbbbbbbbbbbbbb', result)
}
}
const a = new SpecializedContractorA()
a.dailyRoutine()
const b = new SpecializedContractorB()
b.dailyRoutine()

flow error accessing optional prop after truthy check

flow 0.67.1 (but behavior continues to exist in 0.73.1)
Example:
type PropOptional = {
prop?: ComplexType
};
type ComplexType = {
callable: () => void,
anotherCallable: () => void
};
function usePropOptional(arg1: PropOptional) {
if (arg1.prop) {
arg1.prop.callable();
arg1.prop.anotherCallable();
arg1.prop.callable();
}
};
The function checks for the presence of arg1.prop before accessing any properties on arg1.prop. This should be sufficient to verify that arg1.prop is defined.
Flow is fine with the first time an arg1.prop property is accessed, which is the call to arg1.prop.callable() on the first line inside the if block. However, flow generates errors on subsequent attempts to access arg1.prop properties in the exact same if block:
arg1.prop.anotherCallable();
arg1.prop.callable();
I am forced to either prepend each line with a rote arg1.prop && truthy check, or reassign arg1.prop to a local variable inside the if block:
function usePropOptional(arg1: PropOptional) {
if (arg1.prop) {
const reallyExists = arg1.prop;
reallyExists.callable();
reallyExists.anotherCallable();
reallyExists.callable();
}
};
This doesn't feel right. What am I doing wrong or missing?
You can check this in a flow repl here on flow.org.
This is documented in FlowType's Type Refinement section:
Refinement Invalidations
It is also possible to invalidate refinements, for example:
// #flow
function otherMethod() { /* ... */ }
function method(value: { prop?: string }) {
if (value.prop) {
otherMethod();
// $ExpectError
value.prop.charAt(0);
}
}
The reason for this is that we don’t know that otherMethod() hasn’t
done something to our value.
...
There’s a straightforward way to get around this. Store the value
before calling another method and use the stored value instead. This
way you can prevent the refinement from invalidating.
// #flow
function otherMethod() { /* ... */ }
function method(value: { prop?: string }) {
if (value.prop) {
var prop = value.prop;
otherMethod();
prop.charAt(0);
}
}
So the workaround in your final case appears to be the suggested way to avoid this problem.

How to handle 'this' in typescript/js ? need to convert Python code to Typescript

I have this simple code written in python to create a small
Cards game and I want to make the same using typescript but I'm
facing a big problem with 'this' in my main class in Typescript
this is the original python class:
class deck:
card_type=['Hearts ','Diamonds','Spades','Clubs']
card_rank=[2,3,4,5,6,7,8,9,10,'A','J','Q','K']
full_deck=[]
def build_deck(self):
single={}
for card in self.card_type:
for n in self.card_rank:
single={n: card}
self.full_deck.append(single)
shuffle(self.full_deck)
def reshuffle (self):
print('Re-shuffling again!')
shuffle(self.full_deck)
def choose_card(self):
chosen=choice(self.full_deck)
the_index= self.full_deck.index(chosen)
self.full_deck.pop(the_index)
return chosen
def pick_hand(self, number_of_cards):
hand=[]
new_card={}
for i in range(number_of_cards):
new_card = self.choose_card()
hand.append(new_card)
return hand
And in my main game file I do something like this:
from classes import deck
deck1= deck()
deck1.build_deck()
my_hand=deck1.pick_hand(3)
compu_hand=deck1.pick_hand(3)
But when I try to create a similar class in type script I wrote the following:
export class deck {
single_card: {
cType: string;
cNumber: any;
};
fullDeck: any[] = [];
card_type=['Hearts ','Diamonds','Spades','Clubs'];
card_rank=[2,3,4,5,6,7,8,9,10,'A','J','Q','K'];
shuffle() {
let counter = this.fullDeck.length;
// While there are elements in the array
while (counter > 0) {
// Pick a random index
let index = Math.floor(Math.random() * counter);
// Decrease counter by 1
counter--;
// And swap the last element with it
let temp = this.fullDeck[counter];
this.fullDeck[counter] = this.fullDeck[index];
this.fullDeck[index] = temp;
}
// return this.fullDeck;
}
buildDeck (){
for (let t in this.card_type) {
for ( let n in this.card_rank) {
this.single_card.cType = this.card_type[t];
this.single_card.cNumber = this.card_rank[n];
this.fullDeck.push(this.single_card);
console.log(this.single_card);
}
}
// this.shuffle()
}
}
When I try to use the class from the main 'ts' file like so:
import {deck} from './myclasses'
$(document).ready (function(){
let deck1= new deck;
deck1.buildDeck();
});
The console.log call returns the same error :
jQuery.Deferred exception: Cannot set property 'cType' of undefined
TypeError: Cannot set property 'cType' of undefined
at deck.buildDeck (file:///run/media/Work/HTML_Porjects/Game_TS/built/myclasses.js:132:44)
How is it undefined?
What do I need to do to make the Typescript code work like the Python code?
Thanks in advance ...
The error simply states that single_card is undefined, which it is:
class deck {
single_card: {
cType: string;
cNumber: any;
};
// …
}
This will declare the property single_card on the TypeScript class, so that the compiler will accept when you refer to the single_card property of an object of that type (e.g. when doing this.single_card). However, doing so will not actually assign an object of that type to the object. So for the compiled JavaScript (where type information is removed), that property does not exist since you never assign to it. You can easily verify that by looking at the compiled JavaScript there.
So what you would need to do first is assign something to single_card just like you did in the Python version:
this.single_card = {}
However, if you actually look at your Python version, single_card is not a member of the object and it actually doesn’t make any sense for it to be. You are using that to construct the card object you are then adding to the fullDeck array. In Python, it is a local variable, so you should make it a local variable in the TypeScript version too:
buildDeck() {
for (const t of this.card_type) {
for (const n of this.card_rank) {
const single_card = {
cType: this.card_type[t],
cNumber: this.card_rank[n]
};
this.fullDeck.push(single_card);
}
}
}
Btw. you also want to use a for…of loop there instead of for…in.
You should also think about making fullDeck properly typed. Right now it is an array of any, so it can store any object. But what you want to do is actually just keep objects in there that look the way single_card looks. So consider declaring a type for this:
interface SingleCard {
cType: string;
cNumber: any;
}
Then, you can properly type fullDeck:
fullDeck: SingleCard[] = [];

Javascript ES5: How to extend the Function object in order to set a property value on itself, every time the function is executed?

I currently have the following working code:
Function.prototype.GetLastCallerName = function () {
if (!this.arguments || !this.arguments.callee || !this.arguments.callee.caller) return null;
var result = /^function\s+([\w\$]+)\s*\(/.exec(this.arguments.callee.caller.toString());
this.LastCaller = result ? result[1] : 'Anonymous';
return this.LastCaller;
};
I picked up that code from another thread. As you can see, it extends the Function.prototype in order to add a method called GetLastCallerName, which picks the last calling function name and (1) sets it to LastCaller on Function.LastCaller and (2) returns it.
In order to make it work:
function MyFunction1() {
MyFunction1.GetLastCallerName();
console.log(MyFunction.LastCaller);
}
function MyFunction2() {
MyFunction1();
}
MyFunction2();
What I'd like to be able to do: Eliminate the need to use GetLastCallerName() every time and extend Function in order to perform that get every time any function is called.
I'm struggling to follow what you have tried so far with your example, but I think I get the idea of what you'd like to do. Why not leverage classes, and extend on them for your use case. Check out the following example...
class Base {
baseFn() {
console.log('from base');
}
}
class Thing extends Base {
fn1() {
this.baseFn();
}
}
let thingee = new Thing();
thingee.fn1();
So baseFn is now always called when fn1 is called.
JSFiddle Link - class demo
In some of your comments it looks like you are wanting to get the "last calling function's name." How about passing back the instance of the caller itself to the parent? This would surely give you even more flexibility because now you can sculpt your caller however you wish. Check out the following...
class Base {
baseFn(caller) {
console.log(caller.id); // 1
}
}
class Thing extends Base {
constructor(id) {
super();
this.id = id;
}
fn1() {
this.baseFn(this);
}
}
let thingee = new Thing('1');
thingee.fn1();
Now you can add whatever you'd like to your Thing instance, in this case, an object with an id of 1 which can be inspected when fn1 propagates up to baseFn
JSFiddle Link - caller demo

Categories