codecamp

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模式。


Typechecker运行
Typechecker选项
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }