I have declared a JOI schema/bean and cannot use that definition when declaring another schema/bean?
I get a syntax error on "arg: joi.object.schema(TestBean).required()" but can declare an array using a schema like: "argArray: joi.array().items(TestBean).required()"
const TestBean = joi.object().required().keys({
member1: joi.array().items(joi.string().required()),
member2: joi.number().required()
}).unknown(); // allow additional attributes
const BeanMethodDocument = joi.object().required().keys({
arg: joi.object.schema(TestBean).required(),
argArray: joi.array().items(TestBean).required(),
option: joi.string().valid('Empty','Full','HalfFull','HalfEmpty')
});
I am expecting that I can use pre-defined declarations of schemas.
I just need the proper syntax.
You're missing the function call on joi.object.
const BeanMethodDocument = joi.object().required().keys({
arg: joi.object().schema(TestBean).required(),
// ------------^
argArray: joi.array().items(TestBean).required(),
option: joi.string().valid('Empty','Full','HalfFull','HalfEmpty')
});
Related
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)]
);
Here I have defined a method definition with optional named parameter.
class PostBuilder extends StatefulWidget {
final Future<List<Submission>> Function({String? next}) postFetcher;
...
...
}
I can able to invoke that function as expected like below.
class _PostBuilderState extends State<PostBuilder> {
...
...
_fetch() async {
var posts = await widget.postFetcher();
// or
var posts = await widget.postFetcher(next: _items.getLast()?.name);
}
But unfortunately, I cannot figure our how to use it properly and don't know the correct syntax.
PostBuilder((next) => getPosts(next))
This is syntax error that is being thrown by the compiler
error: The argument type 'Future<List<Submission>> Function(dynamic)' can't be assigned to the parameter type 'Future<List<Submission>> Function({String? next})'. (argument_type_not_assignable)
postFetcher takes a named parameter. If you want to assign an anonymous function to it, that anonymous function also must take a named parameter. The syntax for anonymous functions is the same as for named functions (the types for anonymous functions are usually omitted because they can be inferred). You therefore want:
PostBuilder(({next}) => getPosts(next))
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")
[...]
Is it possible to use an enum for the property names of a Joi schema?
For example, given the following string-constants.enum.js file:
export default {
INPUT_BUSINESS_NAME: 'business-name',
INPUT_ADDRESS_LINE_1: 'address-line-1'
// etc...
}
What I want to do is:
const stringConstants = require('path/to/string-constants.enum')
const Joi = require('#hapi/joi')
const businessDetailsSchema = {
[stringConstants.INPUT_BUSINESS_NAME]: Joi.string().required(),
[stringConstants.INPUT_ADDRESS_LINE_1]: Joi.string().required(),
// etc...
}
However, when a fieldset that includes 'business-name' or 'address-line-1' fields is validated it throws an error saying that the fields are't defined in the schema.
If I use this though:
const bname = 'business-name'
const add1 = 'address-line-1'
const businessDetailsSchema = {
[bname]: Joi.string().required(),
[add1]: Joi.string().required(),
// etc...
}
Then it works OK - so it seems to be an issue with reading the values from an imported enum.
Thanks
Fixed it - needed to use import instaed of require to bring the enums in.
Works now
How do I write a class that implements this TypeScript interface (and keeps the TypeScript compiler happy):
interface MyInterface {
(): string;
text2(content: string);
}
I saw this related answer:
How to make a class implement a call signature in Typescript?
But that only works if the interface only has the bare function signature. It doesn't work if you have additional members (such as function text2) to be implemented.
A class cannot implement everything that is available in a typescript interface. Two prime examples are callable signatures and index operations e.g. : Implement an indexible interface
The reason is that an interface is primarily designed to describe anything that JavaScript objects can do. Therefore it needs to be really robust. A TypeScript class however is designed to represent specifically the prototype inheritance in a more OO conventional / easy to understand / easy to type way.
You can still create an object that follows that interface:
interface MyInterface {
(): string;
text2(content: string);
}
var MyType = ((): MyInterface=>{
var x:any = function():string { // Notice the any
return "Some string"; // Dummy implementation
}
x.text2 = function(content:string){
console.log(content); // Dummy implementation
}
return x;
}
);
There's an easy and type-safe way to do this with ES6's Object.assign:
const foo: MyInterface = Object.assign(
// Callable signature implementation
() => 'hi',
{
// Additional properties
text2(content) { /* ... */ }
}
)
Intersection types, which I don't think were available in TypeScript when this question was originally asked and answered, are the secret sauce to getting the typing right.
Here's an elaboration on the accepted answer.
As far as I know, the only way to implement a call-signature is to use a function/method. To implement the remaining members, just define them on this function. This might seem strange to developers coming from C# or Java, but I think it's normal in JavaScript.
In JavaScript, this would be simple because you can just define the function and then add the members. However, TypeScript's type system doesn't allow this because, in this example, Function doesn't define a text2 member.
So to achieve the result you want, you need to bypass the type system while you define the members on the function, and then you can cast the result to the interface type:
//A closure is used here to encapsulate the temporary untyped variable, "result".
var implementation = (() => {
//"any" type specified to bypass type system for next statement.
//Defines the implementation of the call signature.
var result: any = () => "Hello";
//Defines the implementation of the other member.
result.text2 = (content: string) => { };
//Converts the temporary variable to the interface type.
return <MyInterface>result;
})(); //Invokes the closure to produce the implementation
Note that you don't need to use a closure. You could just declare your temporary variable in the same scope as the resulting interface implementation. Another option is to name the closure function to improve readability.
Here's what I think is a more realistic example:
interface TextRetriever {
(): string;
Replace(text: string);
}
function makeInMemoryTextRetriever(initialText: string) {
var currentText = initialText;
var instance: any = () => currentText;
instance.Replace = (newText: string) => currentText = newText;
return <TextRetriever>instance;
}
var inMemoryTextRetriever = makeInMemoryTextRetriever("Hello");