Can we use JSDoc #this when passing a function literal as an argument - jsdoc

With JSDoc, I know you can use #this when declaring a function with
/** #this {{foo: string}} */
const myFunc = function() {
console.log(this.bar) // this will NOT type-check
return this.foo // this will type-check
};
I'd like to be able to do the same when passing a function literal as an argument. This would look something like:
functionDefinedSomewhereElse(/** #this {{foo: string}} */ function() {
return this.foo // this will NOT type-check
});
The above code does not work though…
Is this even possible ?

Related

Ethers how to encode data to bytes parameters

I'm trying to test a piece of generic solidity code, I'm trying to figure out how to encode data properly for bytes parameters.
I have a function in a smart contract which looks like so:
function validateAdditionalCalldata(bytes calldata resolverOptions) external view;
function resolve(uint256 amountIn, bytes calldata resolverOptions) public override returns (uint256 amountOut) {
// Decode the additional calldata as a SwapResolverOptions struct
(SwapResolverOptions memory swapResolverOptions) = abi.decode(resolverOptions, (SwapResolverOptions));
return resolveImplementation(amountIn, swapResolverOptions);
}
This solidity code will generate code which takes in a PromiseOrValue<BytesLike>:
resolve(
amountIn: PromiseOrValue<BigNumberish>,
resolverOptions: PromiseOrValue<BytesLike>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
export type SwapResolverOptionsStruct = {
path: PromiseOrValue<BytesLike>;
deadline: PromiseOrValue<BigNumberish>;
amountIn: PromiseOrValue<BigNumberish>;
amountOutMinimum: PromiseOrValue<BigNumberish>;
inputTokenAddress: PromiseOrValue<string>;
targetAddress: PromiseOrValue<string>;
destinationAddress: PromiseOrValue<string>;
};
I'm wondering how I can encode a specific parameter so I can pass it along to ethers. In typescript I have a set of options that looks like this:
const resolverOptions: SwapResolverOptionsStruct = {
path: '0x00',
//This parameter will be removed in the next deployment
deadline: BigNumber.from(1000),
amountIn: BigNumber.from(100000000),
amountOutMinimum: BigNumber.from(0),
inputTokenAddress: WMATIC_MUMBAI,
destinationAddress: owner.address,
targetAddress: ADDRESS_ZERO,
};
I'm trying to encode this parameters in the following way:
import { defaultAbiCoder } from "#ethersproject/abi";
encodedResolverOptions = defaultAbiCoder.encode(
['SwapResolverOptionsStruct'],
[resolverOptions]
);
However when I try to encode it gets and error:
Error: invalid type (argument="type", value="SwapResolverOptionsStruct",
Note: in my paticular use case I cannot just encoded the whole function call.
I would like to pass my data to the validateAdditionalCalldata how ever the parameter is PromiseOrValue<BytesLike>
How can I encoded my resolverOptions so I can pass it as bytes?
I figured out a few way to do this. I'll list each one:
Resolve the type params using ethers and get function. (if there is a function which has your type in it)
const functionFragment = swapResolverInterface.getFunction("resolveImplementation");
encodedResolverOptions = defaultAbiCoder.encode(
[functionFragment.inputs[1]],
[resolverOptions]
);
Resolve it from an object and a method signature with parameter names:
encodedResolverOptions = defaultAbiCoder.encode(
["(bytes path,uint256 deadline,uint256 amountIn,uint256 amountOutMinimum,address inputTokenAddress,address destinationAddress,address targetAddress)"],
[resolverOptions]
);
Resolve it as an array from the method signature without names Note: this assumes the object was declared in the same order as the parameters, alternatively you can manually construct the array:
encodedResolverOptions = defaultAbiCoder.encode(
["(bytes,uint256,uint256,uint256,address,address,address)"],
[Object.values(resolverOptions)]
);

Correct JSDoc signature for is_string function?

I have the following function of type (obj:any)=>boolean that determines whether an object is string or not.
I want the JsDoc to be "smart" so that whenever I use if(isString(x)){ ...block... }, the highlighter treats x inside the block as a string.
So far I tried this without success:
/** #type {((obj:string)=>true)|((obj:any)=>false)} */
function isString(obj) {
return Object.prototype.toString.call(obj) === "[object String]";
}
How can I do it properly?
The following should do the trick. It uses the Typescript's narrowing with type predicates.
/** #type {(obj: any) => obj is String} */
function isString(obj) {
return Object.prototype.toString.call(obj) === "[object String]";
}

How do I pass a docstring to pybind11::def_property_readonly?

I am trying to add a documented readonly property to a class in a python extension defined using pybind11. Usually this is done by adding a string argument to the define call. However, when I add a string argument to the readonly property definition call, I get template compile errors.
Compiles but doesn't have a docstring:
[...]
.def_property_readonly(
"property_name",
[](){ return ""; })
[...]
Has a docstring but doesn't compile:
[...]
.def_property_readonly(
"property_name",
[](){ return ""; },
std::string("docstring"))
[...]
You have to pass a const char * instead of a std::string.
[...]
.def_property_readonly(
"property_name",
[](){ return ""; },
"const char docstring")
[...]

Using local variables in function built with haxe macro

I have a LangBuilder macro class; it's used to build a langObjects:Map<String, Dynamic> of texts in various languages at compile time, and inject this structure in classes via #:build macro. Every item of the Map has a field for every language supported. So the result is:
#:build(LangBuilder.build())
class Lang{}
trace(Lang.langObjects["hello-world"].en); //outputs "Hello World!"
trace(Lang.langObjects["hello-world"].it); //outputs "Ciao Mondo!"
This works perfectly, but I thought I could make a cleaner job hiding the langObjects structure using a function getLangText with arguments the id of the text (e.g. "hello-world") and the language code (e.g. "it").
So I'm trying to add this function to classes:
public static function getLangText(id:String, lang:String)
Its non-macro version could be expressed as:
public static function getLangText(id:String, lang:String){
var _langObj_id = langObjects[id];
switch(lang){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing Translation";
If i translate this function as a macro with this code:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
switch($i{"lang"}){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing translation";
};
var myFunc:Function = {
args: [{
name: "id",
type: TPath({name: "String", pack: []}),
meta: null
},
{
name: "lang",
type: TPath({name: "String", pack: []}),
meta: null
}],
ret: (macro:String),
expr: macro $code
};
fields.push({
pos: Context.currentPos(),
name: "getLangText",
meta: null,
kind: FieldType.FFun(myFunc),
doc: null,
access: [Access.APublic, Access.AStatic]
});
... it works without problems. However I would like to know how it could be written without the switch, to make it more flexible and to learn something about haxe macros. I have seen some examples where fields could be accessed in macros with $p{} or with object.$fieldName. However the haxe manual warns that the second form could be used only for simple identifiers; for example object.${fieldName} would not work.
So I try this code:
var code = macro {
var l:String = $i{"lang"};
var _langObj_id = langObjects[$i{"id"}];
return _langObj_id.$l;
};
The compiler gives an error
Unknown identifier : l
on the line containing return _langObj_id.$l;.
Then i tried to use the $p{} reification:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
return macro $p{["_langObj_id", $i{"lang"}]};
};
But the error is similar:
Unknown identifier : lang
I can surely change the langObjects structure to Map<String, Map<String, String>> and then change the code to:
var code = macro {
return macro langObjects[$i{"id"}][$i{"lang"}];
};
I think this would work, but now i'm trying to understand why both _langObj_id.$lang and $p{["_langObj_id", $i{"lang"}]} wouldn't work, and what would be the correct way to access a field in a situation like that.
The value of the lang parameter is not known at compile- / macro-time, so I don't see how you could generate a field access expression like langObjects["mytext"].en. At runtime when getLangText() is actually called, lang could be "en", or anything else. So that would still require a switch-case, if-else-chain or reflection to handle all the possible values.
If instead of using being created by a build macro, getLangText() was an expression macro / a macro function, the function call would be evaluated at compile-time, and be replaced with the expression it returns. That would allow you to generate the appropriate field access expression based on the parameters. It could look something like this:
class Macro {
public static var langObjects = ["mytext" => {en: "hello", de: "hallo"}];
public static macro function getLangText(id:String, lang:String) {
return macro {
var langObject = Macro.langObjects[$v{id}];
langObject.$lang;
}
}
}
class Main {
static function main() {
trace(Macro.getLangText("mytext", "en"));
trace(Macro.getLangText("mytext", "de"));
}
}
Compiles to the following on the JS target:
Main.main = function() {
var langObject = Macro.langObjects.get("mytext");
console.log("source/Main.hx:3:",langObject.en);
var langObject1 = Macro.langObjects.get("mytext");
console.log("source/Main.hx:4:",langObject1.de);
};
Perhaps that's what you're looking for? Hard to say without knowing what problem you're trying to solve.

javascript scope and everything about 'this'

I am trying to understand in depth how 'this' works in javascript.
All I have known about this so far is,
Every function has properties and whenever the function executes, it newly defines the this property.
this refers to the object that a function is invoked to (including window object in browser).
this refers to the scope of the object(where the object is defined) instead of referring to the object itself if you use arrow syntax when defining a function because arrow function does not newly defines its own this.
The examples below are to help understanding the behaviour of this
class Example {
constructor() {
this.name = 'John';
}
method1() { //case1 : Closure
console.log(this.name);
function method2() {
console.log(this.name);
}
method2();
}
}
const a = new Example()
a.method1();
function testing(callback) {
return callback();
}
class Example2 {
constructor() {
this.name = 'John';
}
method1() { //case2: callback
console.log(this.name);
testing(function() {
console.log(this.name);
})
}
}
const b = new Example2()
b.method1();
function testing(callback) {
return callback();
}
class Example3 {
constructor() {
this.name = 'John';
}
method1() { //case3: arrow syntax callback
console.log(this.name);
testing(() => {
console.log(this.name);
})
}
}
const c = new Example3()
c.method1(); // logs 'John'
// logs 'John'
function testing(callback) {
return callback();
}
class Example4 {
constructor() {
this.name = 'John';
}
method1() { // case4: calling method as callback
console.log(this.name);
}
render() {
testing(this.method1)
}
}
const d = new Example4()
d.render()
function testing(callback) {
return callback();
}
class Example5 {
constructor() {
this.name = 'John';
this.method1 = this.method1.bind(this);
}
method1() { //case5: bind method && calling method as callback
console.log(this.name);
}
render() {
testing(this.method1)
}
}
const d = new Example5()
d.render()
I wonder how those above cases are different and what the this refers to inside each inner function and callback. Could you please explain about it? thank you :)
Since the in-depth precise explanation can be pretty big and boring, here is an exceptional article by kangax that perfectly lays it out.
And just in case, if you need a short and ultra condensed version of it here goes my short and approximate take:
#
When you call a function the this is determined by the specific base value which is usually pointing to whatever is on the left of the .
in MemberExpression so in x.y() this === x, and in x.y.z() this === x.y.
In case of a simple CallExpression without the ., say just x(),
the base value is implicitly inferred to point to undefined, which in non-strict mode is converted to global window and in strict mode stays the same.
This is the general mental model which should cover 99% of all the day-to-day problems with drawing the this context out correctly.
Now on, to the actual cases:
CASE 1:
a.method1(); call has a base value a so the this inside of its body points to a, so no surprises here.
method2 has implicit base value undefined.method2, thus you have the TypeError which explicitly states that.
CASE 2:
function testing(callback) {
return callback();
}
callback() is called with implicit baseValue undefined, i.e. undefined.callback(),
and since the passed function is declared within class
testing(function() {
console.log(this.name);
})
that triggers the strict mode of code execution, that's why undefined is not converted again to global window, thus we have the same error as before.
CASE 3:
Arrow function
testing(() => {
console.log(this.name);
})
creates a hard binding from the this in enclosing scope,
basically under the hood it's the same as writing:
var _this = this;
testing((function() {
console.log(_this.name);
});
That's why you get the same object resolved as this
CASE 4:
Alright, this one is interesting and needs more mechanics explanation.
So when you pass this.method in:
render() {
testing(this.method1)
}
what you actually pass is not the reference this.method, but the actual underlying Function Object value, to which this reference points to, so
when it gets executed it has its this always pointing to undefined, here look, so it's pretty much "in stone".
And yes of course since this.method1 is declared in strict context again, thanks to enclosing es6 class, undefined remains undefined without conversion to global window.
CASE 5:
Same mechanics as with arrow function. Bind creates a wrapper function, which holds the cached this value, which is not possible to override with .call and .apply, the same as in => function.
Hope this clarifies a bit it all a bit.