Typechecker模式
在编写Hack代码时,通常会启动文件<?hh并开始编写代码。但是,一个顶线实际上在Typechecker解释代码方面非常重要。
部分模式
Hack文件中的代码<?hh以其顶部的四个字符开头,据说处于部分模式。这意味着typechecker会检查一下它可以,但不再有; 它不坚持全面覆盖。部分模式很好用于开始逐渐输入现有的代码。以下是部分模式的规则:
- 您可以与PHP文件完全互操作 - 您可以调用函数并使用Typechecker看不到的类。该代码假设存在于某个<?php文件中。见的讨论assume_php配置选项对于这意味着什么,为什么它可能是不希望,以及如何改变它的讨论。
- 您可以在顶层编写代码(即外部函数和方法),但不会进行类型检查。为了最大化Typechecker在部分模式下检查代码的能力,通常的做法是将所有顶级代码包装到一个主要功能中,并将该功能的调用作为顶级的唯一代码。
- 您可以使用引用&,但是typechecker不会尝试检查它们(为了进行类型检查,它忽略&)。尝试不使用引用,因为您可以轻松地使用它们来打破类型系统。
- 您可以无错误地访问superglobals。
<?hh
namespace Hack\UserDocumentation\TypeChecker\Modes\Examples\Partial;
use \Hack\UserDocumentation\TypeChecker\Modes\Examples\NonHack as NonHack;
// This function isn't type annotated, so callers will be able to do whatever
// they want with its result. However, the typechecker does still look at and
// check the body of the function.
function foo() {
$a = 1;
// This will geneate a type error:
// an int does not allow array append (Typing[4006])
$a[] = 2;
}
class A {
private int $x;
public function __construct() {
$this->x = 9;
}
public function getX(int $y): ?int {
return $y > 4 ? $this->x : null;
}
// You can even have non-type annotated code in the same class as
// type annotated code.
public function notTyped($z) {
return "Hello" . $z;
}
}
function bar(): int {
$a = new A();
// Not typechecked either. So we can pass an int and it will be converted to
// a string by the runtime, luckily.
echo $a->notTyped(3);
// The return value from this call is not typechecked since B is in a PHP
// file -- the typechecker assumes we know what we are doing since the
// annotation is missing.
$b = NonHack\B::getSomeInt();
echo NonHack\php_func(3, $b);
$i = $a->getX($b);
if ($i !== null) {
return $i;
} else {
return 0;
}
}
bar();
Output
string(4) "1004"
Hello33100
请注意,我们已经注释了一些代码,但不是全部。无论该功能本身的注释如何,都会检查推送的代码。
严格模式
hack文件为:
<?hh // strict
意味着Typechecker在该文件中强制执行严格的输入规则。如果在所有可能的情况下,使用严格模式启动新项目 - 如果代码库中的每个文件都处于严格模式,则Typechecker的覆盖范围将最大化,因为它将能够完全检查所有内容,并且在运行时不会出现类型错误。
以下是严格模式的规则:
- 必须对所有函数和方法进行完全注释,即必须完全指定它们的参数和返回类型。任何可以具有类型注释的位置都必须有一个。
- 所有被调用的函数和引用的类必须在Hack文件中定义。因此,例如,如果您的严格模式代码尝试使用<?php文件中定义的类,则会出现错误,因为Typechecker不会查找<?php文件,并且不会知道该类。但是,从严格模式,您可以调用部分和声明模式下的Hack代码。
- 允许在顶层是唯一码require,require_once,include,include_once,namespace,use,和类,函数的定义枚举和常量。
- 没有任何参考&。
- 没有访问superglobals。
严格模式的是你想要的模式。整个Typechecker的好处是可以使用的,应该确保零运行时类型的错误。
<?hh // strict
namespace Hack\UserDocumentation\TypeChecker\Modes\Examples\Strict;
use \Hack\UserDocumentation\TypeChecker\Modes\Examples\NonHack as NonHack;
function foo(): void {
$a = 1;
// This will generate a type error:
// an int does not allow array append (Typing[4006])
$a[] = 2;
}
class A {
private int $x;
public function __construct() {
$this->x = 9;
}
public function getX(int $y): ?int {
return $y > 4 ? $this->x : null;
}
// In partial, this didn't have to be annotated. In strict, it does.
public function notTyped(string $z): string {
return "Hello" . $z;
}
}
function bar(): int {
$a = new A();
// This is typechecked, so we can't pass an string-y int; we must pass a
// string
echo $a->notTyped("3");
// Cannot call these in strict mode:
// Unbound name:
// Hack\UserDocumentation\TypeChecker\Modes\Examples\NonHack\B
// (an object type) (Naming[2049])
$b = NonHack\B::getSomeInt();
// Unbound name:
// Hack\UserDocumentation\TypeChecker\Modes\Examples\NonHack\php_func
// Typing[4107])
echo NonHack\php_func(3, $b);
$i = $a->getX(100);
if ($i !== null) {
return $i;
} else {
return 0;
}
}
// This can't be in strict mode either. You need to put this in partial file
// and include it from this file. For the purposes of this example, though,
// we'll just suppress the error.
/* HH_FIXME[1002] So we can get interesting type-checking errors */
bar();
Output
string(4) "1004"
Hello33100
请注意,我们不能再调用该<?php文件,并且Hack文件中的所有实体都被注释。
声明模式
hack代码为:
<?hh // decl
处于声明模式。声明模式代码没有类型检查。然而,提取了在decl模式下的函数,类等的签名,并且在检查其他代码时被Typechecker使用。在转换使用PHP编写的代码时,声明模式是最有用的:虽然该代码的正文可能具有您不想立即处理的类型错误,但仍然有益于使该代码的签名,至少其唯一存在,对其他代码可见。事实上,Hack的一个非常基本的迁移路径是将所有<?php文件更改为decl模式,然后逐个开始采取每个文件并使其部分。
新的hack代码应该永远写在DECL模式。
<?hh // decl
// Before this was <?php code. Now the typechecker can see the signatures of
// these functions and classes for when Hack calls them, even in strict mode.
namespace Hack\UserDocumentation\TypeChecker\Modes\Examples\Decl;
function php_func($x, $y) {
return $x . $y;
}
class B {
static function getSomeInt() {
return 100;
}
}
<?hh // strict
namespace Hack\UserDocumentation\TypeChecker\Modes\Examples\CallIntoDecl;
require __DIR__ . '/decl.inc.php';
// This actually makes the call to calling_into_decl() since we cannot have
// top level functions in strict mode
use \Hack\UserDocumentation\TypeChecker\Modes\Examples\Decl as Decl;
function calling_into_decl(): string {
// If php_func wasn't in decl mode, then we would get an unbound name error.
// As it is, we can call this function and the typechecker will ensure we are
// passing in the right number of arguments, but not the types of them.
return Decl\php_func("a", "b");
}
<?hh
namespace Hack\UserDocumentation\TypeChecker\Modes\Examples\CallIntoDecl;
var_dump(calling_into_decl());
Output
string(2) "ab"
该示例显示所有三种模式。首先,它显示一个以前存在的声明模式文件<?php。除了标题更改之外,没有添加任何其他内容。然后它显示一个严格的模式文件调用到declare文件。Typechecker知道函数和类的签名,并且可以确保基本的东西,例如是否调用命名实体并传递正确数量的参数。最后,我们有一个部分模式文件实际上以严格模式调用该函数,因为我们不能在严格模式下进行顶层函数调用。
混合模式
如上例所示,模式可以自由混合; 您的项目中的每个文件可以处于不同的Typechecker模式。