Can't figure out with next comparing:
var type = typeof(ValueTuple<int, string>);
if (type.BaseType == typeof(ValueTuple))
// returns 'false', however, 'type.BaseType' is 'System.ValueTuple' at runtime
Who can to explain that?
As with any struct the base type is System.ValueType.
The similarity to the human eye got me confused for a minute or two.
Related
How do I convert the data type if I know the Variant.Type from typeof()?
for example:
var a=5;
var b=6.9;
type_cast(b,typeof(a)); # this makes b an int type value
How do I convert the data type if I know the Variant.Type from typeof()?
You can't. GDScript does not have generics/type templates, so beyond simple type inference, there is no way to specify a type without knowing the type.
Thus, any workaround to cast the value to a type only known at runtime would have to be declared to return Variant, because there is no way to specify the type.
Furthermore, to store the result on a variable, how do you declare the variable if you don't know the type?
Let us have a look at variable declarations. If you do not specify a type, you get a Variant.
For example in this code, a is a Variant that happens to have an int value:
var a = 5
In this other example a is an int:
var a:int = 5
This is also an int:
var a := 5
In this case the variable is typed according to what you are using to initialized, that is the type is inferred.
You may think you can use that like this:
var a = 5
var b := a
Well, no. That is an error. "The variable type can't be inferred". As far as Godot is concerned a does not have a type in this example.
I'm storing data in a json file: { variable:[ typeof(variable), variable_value ] } I added typeof() because for example I store an int but when I reassign it from the file it gets converted to float (one of many other examples)
It is true that JSON is not good at storing Godot types. Which is why many authors do not recommend using JSON to save state.
Now, be aware that we can't get a variable with the right type as explained above. Instead we should try to get a Variant of the right type.
If you cannot change the serialization format, then you are going to need one big match statement. Something like this:
match type:
TYPE_NIL:
return null
TYPE_BOOL:
return bool(value)
TYPE_INT:
return int(value)
TYPE_REAL:
return float(value)
TYPE_STRING:
return str(value)
Those are not all the types that a Variant can hold, but I think it would do for JSON.
Now, if you can change the serialization format, then I will suggest to use str2var and var2str.
For example:
var2str(Vector2(1, 10))
Will return a String value "Vector2( 1, 10 )". And if you do:
str2var("Vector2( 1, 10 )")
You get a Variant with a Vector2 with 1 for the x, and 10 for the y.
This way you can always store Strings, in a human readable format, that Godot can parse. And if you want to do that for whole objects, or you want to put them in a JSON structure, that is up to you.
By the way, you might also be interested in ResourceFormatSaver and ResourceFormatLoader.
The TypeScript team has been doing great work on string literal typing in recent updates (4.1 & 4.2). I am wondering if there is a way to type a fixed length string.
Ex.
type LambdaServicePrefix = 'my-application-service';
type LambdaFunctionIdentifier = 'dark-matter-upgrader';
type LambdaFunctionName = `${LambdaServicePrefix}-${LambdaFunctionIdentifier}`; // error: longer than 32 characters...
How I imagine it would go is something like, Array<64, string>;. TypeScript has the Tuple type so as an array I could fix length of an array. [string, ... string * 62, string].
type FutureLambdaIdType = `${LambdaServicePrefix}-${string[32]}`;
UPDATED to reflect better recursive conditional type support
There are still, as of TS 4.7, no regular-expression-validated string types in TypeScript. Template literal types handle some, but not all, of the use cases for such regex types. If you have a situation like this where template literal types are insufficient, you might want to go to microsoft/TypeScript#41160 and describe your use case. The idea of a "string whose maximum length is N characters" for some N extends number would be easy enough to express with regex types, but is not easily achievable with template literals.
Still, let's see how close we can get.
A major roadblock stands in the way. TypeScript cannot easily represent the set of all strings less than N characters as a specific type StringsOfLengthUpTo<N>. Conceptually any given StringsOfLengthUpTo<N> is a large union, but since the compiler balks at unions with more than ~10,000 members, you can only describe strings of up to a few characters this way. Assuming you want to support the 95 characters of 7-bit printable ASCII, you will be able to represent StringsOfLengthUpTo<0>, StringsOfLengthUpTo<1>, and even StringsOfLengthUpTo<2>. But StringsOfLengthUpTo<3> would exceed the compiler's capacity, since it would be a union of over 800,000 members. So we have to give up on specific types.
Instead we can think of our type as a constraint used with generics. We need a type like TruncateTo<T, N> which takes a type T extends string and an N extends number and returns T truncated to N characters. Then we can constrain T extends TruncateTo<T, N> and the compiler would automatically warn on too-long strings.
It used to be that shallow recursion limits would prevent us from writing TruncateTo<T, N> for N greater than about 20 or so, but TypeScript 4.5 introduced support for tail recursion elimination on conditional types. That means we can write TruncateTo<T, N> by adding some extra accumulator arguments like this:
type TruncateTo<T extends string, N extends number,
L extends any[] = [], A extends string = ""> =
N extends L['length'] ? A :
T extends `${infer F}${infer R}` ? (
TruncateTo<R, N, [0, ...L], `${A}${F}`>
) :
A
This works by having an A accumulator to store the string we're building up, and an L arraylike accumulator that keeps track of how long that A string is (string literal types don't have a strongly typed length property, see ms/TS#34692 for the relevant request). We build up A one character at a time until we either run out of the original string, or until we reach a length of N. Let's see it in action:
type Fifteen = TruncateTo<"12345678901234567890", 15>;
// type Fifteen = "123456789012345"
type TwentyFive = TruncateTo<"123456789012345678901234567", 25>;
// type TwentyFive = "1234567890123456789012345"
We can't directly write T extends TruncateTo<T, N> as TypeScript complains that this is a circular constraint. But we can at least write a helper function like this:
const atMostN = <T extends string, N extends number>(
num: N, str: T extends TruncateTo<T, N> ? T : TruncateTo<T, N>
) => str;
and then you could call atMostN(32, "someStringLiteral") and it would either succeed or warn based on the the length of the string literal argument. Note that the str input is of a weird conditional type, whose sole purpose is to avoid the circular constraint. T is inferred from str, and then checked against TruncateTo<T, N>. If it succeeds, great. Otherwise, we give str the type of TruncateTo<T, N>, and we'll see an error message. It works like this:
const okay = atMostN(32, "ThisStringIs28CharactersLong"); // okay
type Okay = typeof okay; // "ThisStringIs28CharactersLong"
const bad = atMostN(32, "ThisStringHasALengthOf34Characters"); // error!
// -------------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// '"ThisStringHasALengthOf34Characters"' is not assignable to parameter of type
// '"ThisStringHasALengthOf34Characte"'.
type Bad = typeof bad; // "ThisStringHasALengthOf34Characte"
Is it worth it? Maybe. The original answer here had to do some unsavory things to get even a fixed-length check. The current one isn't so bad, but it's still a bunch of effort to get a compile-time check. So you might still have a use case for regex-validated string types.
Playground link to code
There is no way to represent fixed-length strings with Typescript. There is a very upvoted proposal here, but still this feature has not been released.
If the length is very little, there are some workarounds suchs as the following:
type Char = 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z'
type String3 = `${Char}${Char}${Char}`
const a: String3 = 'aa' // error
const b: String3 = 'bbbbb' // error
const c: String3 = 'ccc' // OK
const d: String3 = 'abc' // OK
But you can't handle big lengths since you will run into a "Expression produces a union type that is too complex to represent" error.
It is impossible to limit the length of string by typing or typescript utils.
You can, however, use regex to validate the string (including length):
/^([a-zA-Z0-9_-]){1,64}$/
type IsThirteen<T extends number> = 13 extends T ? true : never
type IsFifteen<T extends number> = 15 extends T ? true : never
type LengthOfString<S extends string, T extends string[] = []> = S extends `${string}${infer R}`
? LengthOfString<R, [...T, string]>
: T['length'];
type IsLengthThirteenOrFifteen<T extends string> = true extends IsThirteen<LengthOfString<T>>
? T
: true extends IsFifteen<LengthOfString<T>>
? T
: never
function IsLengthThirteenOrFifteenGuard <T extends string>(a: IsLengthThirteenOrFifteen<T>) {
return a;
}
const b = IsLengthThirteenOrFifteenGuard('1131111111111')
Sources:
StringOfLength
Playground
Trying to fix a HH complain... Basically the code is doing something similar to this
Sfirstgroup = idx($largegroup, "first");
$final_thing = null
if(HH\is_any_array(Sfirstgroup) && Sfirstgroup){
/*HH_FIXME[4110] Error revealed by is_arry refining to varray_or_darray */
$final_thing = idx(Sfirstgroup[0],"final_thing")
}
I think it must have some thing to with firstgroup[0] doesn't have a collection type. but couldn't figure out how to fix this...
Thanks a lot!
You have two type errors:
Indexing with square bracket syntax $firstgroup[0] doesn't work very well with the inferred type from is_any_array, which infers wildcard generics for the key type (i.e. it's a KeyedContainer<_,_>). Either change $a[0] to idx($a, 0) or use is_vec_or_varray if all your keys are int, since it refines to vec<_>.
You need to prove $firstgroup[0] is a KeyedContainer to use your second idx call for $final_thing. The refinement checks like is_any_array won't work on an indexed variable (like $firstgroup[0]), so you have to make a temporary variable to refine it.
All in all, here's one possible solution:
$firstgroup = idx($largegroup, "first");
$final_thing = null;
if(HH\is_vec_or_varray($firstgroup)){
$first_item = $firstgroup[0];
if(HH\is_any_array($first_item)) {
$final_thing = idx($first_item,"final_thing");
}
}
The C# MSDN documentation states that widening conversions are implicit and dont require an explicit cast. Accordingly, I find that the following code works without giving any errors:
public void MyMethod(int x)
{
float f = x; //widening conversion, works implicitly as expected.
...
}
But, the following does not seem to work, even though this also appears to me to fall under the category of a widening conversion.
public static void MyMethod(int x)
{
object o = x; // implicit conversion - works.
float f = (float)o; // implicit conversion expected here also - but doesnt work...
}
In the above second piece of code, I would expect an implicit conversion to happen from the int data stored in 'o' to the type specified in the explicit cast(float). But this doesnt happen and this code throws an InvalidCastException. Why is this so? I can understand an exception being thrown when 'o' is assigned to 'f' without any cast. But if a cast is specified explicitly and converting to that cast requires an implicit conversion (i.e. int to float) which is supported by the language, why is an exception thrown ?
Thanks.
casts do different things at different times. This line:
float f = (float)o;
Is not attempting to change the type of o - it's attempting to unbox a float. Unfortunately, you can only (within a few wiggles1) unbox the same type of value that was boxed - a boxed int has to be unboxed as an int.
You would instead have to do:
float f = (int)o;
Where the (int) is performing the unbox, and then the implicit conversion can occur from int to float, as per your first example.
For more, read Boxing and Unboxing:
Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object and stores it on the managed heap. Unboxing extracts the value type from the object. Boxing is implicit; unboxing is explicit...
1 There are some rules about Enums and their underlying type which I can't remember and won't ever deliberately use.
I get the following error when compiling the std::find_if function:
error C2451: conditional expression of type 'overloaded-function' is illegal
The code looks like this:
typedef std::vector<boost::shared_ptr<node> >::iterator nodes_iterator;
nodes_iterator node_iter = std::find_if(_request->begin(), _request->end(), boost::bind(&RequestValues::is_parameter, this));
bool RequestValues::is_parameter(nodes_iterator iter)
{
return ((*iter)->name.compare("parameter") == 0);
}
It seems to have something to do with the predicate function passed to the std::find_if, however I cannot figure out what is wrong, can anybody help?
node is a struct containing some values.
You should use _1, not this when binding, and take a value_type as an argument of your function.
If this is a class or struct member function, then bind(func, this, _1) maybe? But if it is a class member function it should probably be static because it needn't state.
The comparison function you provide to find_if should not take in an iterator, but rather the value that the iterator is pointing at (or even better, a const reference to it). For example, when writing a predicate for find_if over a range of ints, the comparison should take in an int rather than vector<int>::iterator. Changing your comparison function to work on shared_ptr<node>s might not fix all your errors, but it should at least account for some of them.
That function's signature should be
bool RequestValues::is_parameter(boost::shared_ptr<node>);
i.e., it doesn't take an iterator, but the iterator's value_type.