Any
any holds any value; the cost is that the value’s type is unknown at compile time.
Storing Values and is Tests
var x: any = 42;
print(x);
x = "now some text";
print(x);
print(x is string);
x = 3.5;
print(x is float);42
now some text
True
Trueis tests whether the value has type float and returns a bool. A value stored in any loses its concrete type information at compile time, and calling its methods directly is rejected by the compiler. For how to use a value once narrowed, see Patterns.
Member access on any is rejected outright at compile time; narrow with is first, then take the member. Treat any as a last resort.
Conversions with an any source (o is T, o as T, the checked conversion T(o)) all go through a runtime check; see Conversions.
The Shape of is Narrowing
v is T type-tests an any value; when the test passes, the narrowed value can be bound in a direct condition or an && chain:
func Describe(v: any) -> string
{
if v is int n
{
return $"int {n}";
}
if v is string s
{
return $"string {s}";
}
return "something else";
}
print(Describe(42));
print(Describe("word"));
print(Describe(2.5));int 42
string word
something elseNarrowing only works in direct conditions and && chains: the negated form !(v is T p) and the pure bool position (val b = v is T p;) are not allowed. For the latter, use the binding-free v is T.
Boxing Semantics
Scalars inside any are boxed: at runtime every value carries its real type, and the is test and narrowing judge by it, in the same family as generic erasure. A value that matches no branch falls through to the fallback string (2.5 above is neither int nor string).
typeof and Type Identity
print(typeof(int) == typeof(int), typeof(string) == typeof(int));
val v = 3.5;
print(v is float);
print(v is int);True False
True
Falsetypeof(T) produces the Type value of a type, which can be compared and passed around. The is decision follows the compatibility between the value and the target type: 3.5 is a float, not an int.
A value’s real runtime type can be queried with Reflect.TypeOf; the mapping rules are covered in Reflection.
Heterogeneous Containers
list<any> holds heterogeneous values, and iterating with element-by-element is narrowing is the most common shape:
val bag = new list<any>();
bag.Add(1);
bag.Add("word");
bag.Add([1, 2]);
for item in bag
{
if item is int n
{
print("int", n);
}
elif item is string s
{
print("str", s);
}
else
{
print("other");
}
}int 1
str word
otherThe array literal [1, 2] is an int[], neither int nor string, so it lands in the fallback.
Costs and Limits
- Member access is rejected at compile time:
.field/.method()on any is never accepted; narrow withisfirst. any is not a universal container, so avoid overusing it. - Exempt from null-check narrowing: null checks involving any do not go through the nullable-narrowing state machine;
any?must be checked on its own. - Once a container value is stored in any, generic instantiation tests such as
is list<int>never match (the runtime does not distinguish by type arguments); do not rely on them for exact type checks.
