Expressions And Operators: Type Assertions
Hack provides the is
and as
operators for inspecting types at
runtime. To convert primitive types, see casting.
The type checker also understands is
and as
, so it will infer
precise types.
Checking Types with is
The is
operator checks whether a value has the type specified, and
returns a boolean result.
1 is int; // true
'foo' is int; // false
1 is num; // true
1.5 is num; // true
'foo' is num; // false
The type checker understands is
and refines
values inside conditionals or after
invariant
calls.
function transport(Vehicle $v): void {
if ($v is Car) {
$v->drive();
} else if ($v is Plane) {
$v->fly();
} else {
invariant($v is Boat, "Expected a boat");
$v->sail();
}
}
A common pattern with is
refinement is to use nonnull
rather than
an explicit type.
function transport(?Car $c): void {
if ($c is nonnull) {
// Infers that $c is Car, but saves us
// repeating the name of the type.
$c->drive();
}
}
Enums
For enums, is
also checks that the value is valid.
enum MyEnum: int {
FOO = 1;
}
function demo(): void {
1 is MyEnum; // true
42 is MyEnum; // false
'foo' is MyEnum; // false
}
Generics
Since is
provides a runtime check, it cannot be used with erased
generics. For generic types, you must use _
placeholders for type
parameters.
$v = vec[1, 2, 3];
// We can't use `is vec<int>` here.
$v is vec<_>; // true
If you need to check inner types at runtime, consider using reified generics instead.
Tuples
For tuples and shapes, is
validates the size and recursively validates every field in the value.
$x = tuple(1, 2.0, null);
$x is (int, float, ?bool); // true
$y = shape('foo' => 1);
$y is shape('foo' => int); // true
Aliases
is
also works with type aliases and type constants, by testing
against the underlying runtime type.
type myint = int;
function demo(): void {
1 is myint; // true
}
Enforcing Types with as
and ?as
as
performs the same checks as is
.
However, it throws TypeAssertionException
if the value has a
different type. The type checker understands that the value must have
the type specified afterwards, so it
refines the value.
1 as int; // 1
'foo' as int; // TypeAssertionException
as
enables you to narrow a type.
// Normally you'd want to make transport take a Vehicle
// directly, so you can check when you call the function.
function transport(mixed $m): void {
// Exception if not a Vehicle.
$v = $m as Vehicle;
if ($v is Car) {
$v->drive();
} else {
// Exception if $v is not a Boat.
$v as Boat;
$v->sail();
}
}
Hack also provides ?as
, which returns null
if the type does not match.
1 ?as int; // 1
'foo' ?as int; // null
Note that as
can also be used in type signatures when using
generics.
Legacy Type Predicates
Hack also provides type predicate functions is_int
, is_bool
and so
on. You should use is
instead.