Refining
Refining类型基本上确定一种类型的值也是另一种类型。
假设您想要使用给定类型并将该特定类型细化为另一种兼容类型。Hack允许通过在控制流情况下使用三个构造:
- 检查 null
- 类型查询(例如,通过is_float())
- 运用 instanceof
不可否认的
请记住,可空类型允许变量的值为其类型或null。有时您只想使用null该类型的非部分内容。您可以使用空检查来细化可空类型。
<?hh
namespace Hack\UserDocumentation\Types\Refining\Examples\Nullable;
function foo(?int $x): int {
$a = 4;
if ($x !== null) { // refine $x to just an int by verifying it is not null
return $x + $a; // guaranteed that $x is not null now
}
return $a;
}
var_dump(foo(5));
Output
int(9)
混合为原始
请记住,它mixed代表任何可注释类型(可空类型除外)。mixed可以通过使用内置类型的查询功能来将其细化为更具体的原始类型is_int(), is_float(), is_string()等等
<?hh
namespace Hack\UserDocumentation\Types\Refining\Examples\Mixed;
function foo(mixed $x): int {
$a = 4;
if (is_int($x)) { // refine $x to int by checking to see if $x is an int
return $x + $a;
} else if (is_bool($x)) {
return (int) $x + $a; // know it is a bool, so can do safe cast
}
return $a;
}
var_dump(foo(true));
Output
int(5)
对象实例检查
有时你想知道一个对象是一个父类的子对象还是实现一个特定的接口。您可以使用instanceof支票来帮助做出这一决定。
<?hh
namespace Hack\UserDocumentation\Types\Refining\Examples\Obj;
interface I {
public function foo(): string;
}
class Base implements I {
public function foo(): string {
return "Base";
}
}
class Child extends Base {
// The __Override attribute is discussed in the section on attributes
// TODO: LINK HERE WHEN READY!
<<__Override>>
public function foo(): string {
return "Child";
}
}
function bar(Base $b): Child {
if ($b instanceof Child) { // refine $b to Child, a subclass of Base
echo $b->foo(); // "Child"
return $b;
}
echo $b->foo(); // "Base"
return new Child();
}
function baz(I $i): Child {
// guarantee that the interface will be a Child
invariant($i instanceof Child, "Not Child");
echo $i->foo(); // "Child"
return $i;
}
function refine_object(): void {
$c = new Child();
bar($c);
bar(new Base());
baz($c);
}
refine_object();
Output
ChildBaseChild
instanceof Gotcha
看看这个例子。
<?hh
namespace Hack\UserDocumentation\Types\Refining\Examples\Unresolved;
interface I {
public function i_method(): bool;
}
abstract class Base {
abstract public function foo(): string;
}
class Child1 extends Base implements I {
<<__Override>>
public function foo(): string {
return "Child1";
}
public function i_method(): bool {
return true;
}
}
class Child2 extends Base {
<<__Override>>
public function foo(): string {
return "Child2";
}
}
function bar(Base $b): void {
if ($b instanceof I) { // refine $b to interface I, but makes $b unresolved
echo $b->i_method();
}
// This is a type error!
// Given the instanceof check above, we have now made $b unresolved, a union
// between a type of I and Base. So we can only call methods common to both.
// which in this case there are none.
echo $b->foo();
}
function unresolved(): void {
$c = new Child1();
bar($c);
}
unresolved();
Output
1Child1
即使bar()
是在通过了Base
,和所有儿童Base
实行foo()
,一旦instanceof
支票上完成$b
,并检查返回true
时,typechecker必须假设$b
现在是一个未解决的类型两者的Base
和I
。而且由于不是所有的执行者I
都必须处于层次结构中Base
,我们不能保证foo()
不再有可用性。
Invariant
还有一个特殊功能:
invariant(<bool expression of fact>, "Message if not")
可以在外部控制流情况下使用。它本质上是对类型检查器的一个断言,即你在布尔语句中声明的是事实和真实的。
在上述任何精炼方案中,可以使用invariant()与条件检查相反的方法。