hack類型系統(tǒng)

2018-10-12 11:02 更新

在Hack中描述類型主要通過Hack源代碼中的顯式注釋來完成。Hack有很多可能的注釋類型。您可以在我們的表格中查看每種類型的摘要。

Common Primitives

PHP中可用的主要基本類型可在Hack中作為顯式類型注釋使用。這些包括:

  • bool
  • int
  • float
  • string
  • array
  • resource
<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Primitive;

class A {
  protected float $x;
  public string $y;

  public function __construct() {
    $this->x = 4.0;
    $this->y = "Day";
  }
  public function foo(bool $b): float {
    return $b ? 2.3 * $this->x : 1.1 * $this->x;
  }
}

function bar(): string {
  // local variables are inferred, not explicitly typed
  $a = new A();
  if ($a->foo(true) > 8.0) {
    return "Good " . $a->y;
  }
  return "Bad " . $a->y;
}

var_dump(bar());

Output

string(8) "Good Day"

Alias Primitives

Hack 不支持Alias primitives。因此,以下不是要在類型注釋中使用的有效類型:

  • boolean
  • integer
  • real
  • double

void

void是一種特殊的原始類型,這意味著函數(shù)或方法不返回可觀察值。您可以return;在void功能中使用。

注意:void只能用于方法或函數(shù)返回。它不適用于屬性或參數(shù)。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Void;

class A {
  protected float $x;
  public string $y;

  public function __construct() {
    $this->x = 4.0;
    $this->y = "Day";
  }
  public function foo(bool $b): float {
    return $b ? 2.3 * $this->x : 1.1 * $this->x;
  }
}

// void can only be used as a return types
function bar(): void {
  // local variables are inferred, not explicitly typed
  $a = new A();
  if ($a->foo(true) > 8.0) {
    echo "Good " . $a->y;
  } else {
    echo "Bad " . $a->y;
  }
}

bar();

Output

Good Day

In Async

async函數(shù)返回是比較常見的Awaitable<void>

。這意味著雖然功能本身正在等待返回,但是等待的結(jié)果將沒有價值。這實(shí)際上意味著異步函數(shù)做了一些不需要調(diào)用者返回值的異步操作。

noreturn

noreturn是一種特殊的原始類型,這意味著函數(shù)或靜態(tài)方法從不返回值。類似于void,但是你甚至不能使用return;具有返回類型的函數(shù)noreturn。

noreturn用于表示給定的函數(shù)或靜態(tài)方法總是引發(fā)異常,或以某種方式終止函數(shù)本身中的程序。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\NoReturn;

class A {
  protected float $x;
  public string $y;

  public function __construct() {
    $this->x = 4.0;
    $this->y = "Day";
  }
  public function foo(bool $b): float {
    return $b ? 2.3 * $this->x : 1.1 * $this->x;
  }

  // no return cannot be on an instance method
  // only functions and static class methods
  public static function baz(bool $b): noreturn {
    if ($b) {
      throw new \Exception("No Return");
    } else {
      exit(1);
    }
    return; // Even this will cause type-errors
  }
}

// void can only be used as a return types
function bar(): void {
  // local variables are inferred, not explicitly typed
  $a = new A();
  if ($a->foo(true) > 8.0) {
    echo "Good " . $a->y;
  } else {
    echo "Bad " . $a->y;
  }
  A::baz(false);
}

bar();

Output

Good Day

注意:

只有靜態(tài)方法和功能

noreturn只能用于函數(shù)或靜態(tài)方法返回。

實(shí)例方法不能是noreturn。這是由于類型檢查器的分析階段發(fā)生的順序。在控制流分析期間無法確定實(shí)例方法調(diào)用的返回類型,因?yàn)樗枰雷髠?cè)的類型->,并且類型推斷的結(jié)果尚不可用。調(diào)用靜態(tài)方法不是問題,因?yàn)樵谕茢囝愋椭翱梢越鉀Q這些問題。

noreturn 不適用于屬性或參數(shù)。

對象

您可以使用任何內(nèi)置或自定義類或接口的名稱。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Obj;

class Z {
  public function create_A(): A {
    return new A();
  }
}

class A {
  protected float $x;
  public string $y;

  public function __construct() {
    $this->x = 4.0;
    $this->y = "Day";
  }
  public function foo(bool $b): float {
    return $b ? 2.3 * $this->x : 1.1 * $this->x;
  }
}

// We are taking a Z and returning an object of type A
function baz(Z $z): A {
  return $z->create_A();
}

function bar(): string {
  // local variables are inferred, not explicitly typed
  $z = new Z();
  $a = baz($z);
  if ($a->foo(true) > 8.0) {
    return "Good " . $a->y;
  }
  return "Bad " . $a->y;
}

var_dump(bar());

Output

string(8) "Good Day"

mixed

mixed本質(zhì)上是一個全部的類型,表示任何可能的Hack值(包括null和void)。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Mixed;

class A {
  public float $x;
  protected string $y;

  public function __construct() {
    $this->x = 4.0;
    $this->y = "Day";
  }
  // mixed is the most lax type. Use it only when necessary
  public function foo(bool $b): mixed {
    return $b ? 2.3 * $this->x : $this->y;
  }
}

function bar(): string {
  // local variables are inferred, not explicitly typed
  $a = new A();
  $v = $a->foo(false);
  // Since A::foo() returns a mixed, we need to do various checks to make sure
  // that we let the typechecker know understand what is coming back.
  if (is_float($v)) {
    return "No String";
  }
  invariant(is_string($v), "Something went wrong if this isn't true");
  return "Good " . $v;
}

var_dump(bar());

Output

string(8) "Good Day"

稀疏使用

有一些有用的用途mixed,但是一般來說,您希望盡可能具體地使用您的打字,因?yàn)轭愋蜋z查器只能這么做,mixed因?yàn)樗募s束是如此松散。

this

this只能用作類的方法的返回類型注釋。this表示該方法返回定義了該方法的同一個類的對象。

返回的主要目的this是允許在類本身或其子類的實(shí)例上鏈接方法調(diào)用。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\ThisChaining;

class Vehicle {
  private ?int $numWheels;
  private ?string $make;

  public function setNumWheels(int $num): this {
    $this->numWheels = $num;
    return $this;
  }

  public function setMake(string $make): this {
    $this->make = $make;
    return $this;
  }
}

class Car extends Vehicle {
  private ?bool $autoTransmission;

  public function setAutomaticTransmission(bool $automatic): this {
    $this->autoTransmission = $automatic;
    return $this;
  }
}

class Hybrid extends Car {
  private ?bool $pluggable;

  public function setPluggable(bool $pluggable): this {
    $this->pluggable = $pluggable;
    return $this;
  }

  public function drive(): void {}
}


function run(): void {
  $h = new Hybrid();
  // $h->NumWheels(4) returns the instance so you can immediately call
  // setMake('Tesla') in a chain format, and so on. Finally culminating in an
  // actionable method call, drive().
  $h->setNumWheels(4)
    ->setMake('Tesla')
    ->setAutomaticTransmission(true)
    ->setPluggable(true)
    ->drive();
  var_dump($h);
}

run();

Output

object(Hack\UserDocumentation\Types\TypeSystem\Examples\ThisChaining\Hybrid)#1 (4) {
  ["pluggable":"Hack\UserDocumentation\Types\TypeSystem\Examples\ThisChaining\Hybrid":private]=>
  bool(true)
  ["autoTransmission":"Hack\UserDocumentation\Types\TypeSystem\Examples\ThisChaining\Car":private]=>
  bool(true)
  ["numWheels":"Hack\UserDocumentation\Types\TypeSystem\Examples\ThisChaining\Vehicle":private]=>
  int(4)
  ["make":"Hack\UserDocumentation\Types\TypeSystem\Examples\ThisChaining\Vehicle":private]=>
  string(5) "Tesla"
}

this一個static方法意味著一個類方法返回與調(diào)用方法相同類的對象。您可以使用它從static返回類似的類方法返回一個對象的實(shí)例new static()。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\ThisStatic;

class A {
  protected float $x;
  public string $y;

  // typechecker error if constructor isn't final because new static() cannot
  // be called to return an instance of a subclass
  final protected function __construct() {
    $this->x = 4.0;
    $this->y = "Day";
  }

  public function foo(bool $b): float {
    return $b ? 2.3 * $this->x : 1.1 * $this->x;
  }

  // The this type annotation allows you to return an instance of a type
  public static function create(int $x): this {
    $instance = new static();
    if ($x < 4) {
      $instance->x = floatval($x);
    }
    return $instance;
  }
}

function bar(): string {
  // local variables are inferred, not explicitly typed
  // There is no public constructor, so call A's create() method
  $a = A::create(2);
  if ($a->foo(true) > 8.0) {
    return "Good " . $a->y;
  }
  return "Bad " . $a->y;
}

var_dump(bar());

Output

string(7) "Bad Day"

num

num是特殊的聯(lián)合類型int和float。通常,在Hack中,ints和floats是不兼容的類型。但是,實(shí)現(xiàn)了許多數(shù)值操作函數(shù)的工作方式類似,無論你是傳遞一個整數(shù)還是一個浮點(diǎn)數(shù)。num用于這些情況。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Num;

class A {
  protected num $x;
  public string $y;

  public function __construct(num $x) {
    $this->x = $x;
    $this->y = "Day";
  }
  public function foo(bool $b): num {
    return $b ? 2.3 * $this->x : 1.1 * $this->x;
  }
  // The $x property can be either a float or int
  public function setNum(num $x): void {
    $this->x = $x;
  }
}

function check(A $a): string {
  if ($a->foo(true) > 8.0) {
    return "Good " . $a->y;
  }
  return "Bad " . $a->y;
}

function bar(): string {
  // local variables are inferred, not explicitly typed
  // Setting the $x property in A to an int
  $a = new A(4);
  $ret = check($a);
  // Now setting to a float
  $a->setNum(0.4);
  $ret .= "##" . check($a);
  return $ret;
}

var_dump(bar());

Output

string(17)“Good Day ## Bad Day”

arraykey

arraykey是特殊的聯(lián)合類型int和string。數(shù)組和集合類型可以由int或鍵入string。假設(shè),例如,對數(shù)組執(zhí)行了一個操作來提取密鑰,但是你不知道密鑰的類型。你被使用mixed或做某種重復(fù)的代碼。arraykey解決了這個問題。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\ArrayKey;

class A {
  protected float $x;
  public string $y;

  public function __construct(float $x) {
    $this->x = $x;
    $this->y = "Day";
  }
  public function foo(bool $b): float {
    return $b ? 2.3 * $this->x : 1.1 * $this->x;
  }
}

// This function can return either a string or an int since it is typed to
// return an arraykey
function bar(): arraykey {
  // local variables are inferred, not explicitly typed
  $a = new A(0.9);
  if ($a->foo(true) > 8.0) {
    return "Good " . $a->y;
  }
  return 5;
}

var_dump(bar());

Output

int(5)

XHP

鍵入XHP對象時使用兩個XHP接口:XHPChild和XHPRoot。

XHPRoot 是任何對象,它是XHP類的一個實(shí)例。

XHPChild是echoXHP上下文(例如,echo <div>{$xhpobj}</div>;)中的一組有效類型。這包括原始類型string,int以及float這些類型的數(shù)組加上任何XHP對象。

<?hh

// Namespaces and XHP have issues right now

// A custom class extends :x:element and has a render method that returns
// XHPRoot so that you can do something like echo "<custom-class />;" This
// automatically calls the render method
class :ts-simple-xhp extends :x:element {
  public function render(): XHPRoot {
    return <b>Simple</b>;
  }
}

class TSPage {
  protected string $link;
  protected string $title;

  public function __construct(string $title, string $link) {
    $this->link = $link;
    $this->title = $title;
  }

  // return XHPChild when rendering a UI element and the elements
  // of that render are valid for XHP (e.g., strings, arrays of ints, etc.)
  public function render_page(): XHPChild {
    return <div>{$this->title}...{$this->link}</div>;
  }

  public function get_simple(): XHPRoot {
    return <ts-simple-xhp />;
  }
}

function ts_xhp_sample(): void {
  $p = new TSPage("Test XHP", "http://internet.org");
  echo $p->render_page();
  echo PHP_EOL;
  echo $p->get_simple();
}

ts_xhp_sample();

Output

<div>Test XHP...http://internet.org</div>
<b>Simple</b>

Nullable

nullable類型由?放置為類型本身的前綴(例如,?int)來表示。這只是意味著該值可以是該類型或null.

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Nullable;

class A {
  protected float $x;
  public string $y;

  public function __construct() {
    $this->x = 4.0;
    $this->y = "Day";
  }

  // We can pass a nullable as a parameter as well as being nullable on the
  // return type. Properties can also be nullable
  public function foo(?bool $b): ?float {
    return ($b || $b === null) ? 2.3 * $this->x : null;
  }
}

// The ? means that the function can return null in addition to the string
function bar(): ?string {
  // local variables are inferred, not explicitly typed
  $a = new A();
  if ($a->foo(null) === null) {
    return null;
  }
  return "Good " . $a->y;
}

var_dump(bar());

Output

string(8) "Good Day"

什么不能為空?

void,noreturn不能為空,因?yàn)樗黱ull是一個有效且可觀察的返回值。

至于mixed已經(jīng)允許值null,也可以不寫?mixed。

泛型

泛型允許特定的代碼以類型安全的方式對付多種類型。根據(jù)指定的類型參數(shù),通用類型可以對應(yīng)一種類型或許多類型。Box<T>例如,可以傳遞給它的類型是最容許的。array<int>是最少的允許,因?yàn)閕nt只允許放置在數(shù)組中。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Generics;

// This is a generic class that is parameterized by T. T can be bound to any
// type, but once it is bound to that type, it must stay that type. It
// can be bound to mixed.
class Box<T> {

  private array<T> $contents;

  public function __construct() {
    $this->contents = array();
  }

  public function put(T $x): void {
    $this->contents[] = $x;
  }

  public function get(): array<T> {
    return $this->contents;
  }
}

// This is a generic function. You parameterize it by putting the type
// parameters after the function name
function gift<T>(Box<T> $box, T $item): void {
  $box->put($item);
}

function ts_generics_1(): array<string> {
  $box = new Box();
  gift($box, "Hello");
  gift($box, "Goodbye");
  // can't do this because the typechecker knows by our return statement and
  // our return type that we are binding the Box to a string type. If we did
  // something like ": array<arraykey>", then it would work.
  // This will work when running in HHVM though.
  gift($box, 3);
  return $box->get();
}

function ts_generics_2(): array<arraykey> {
  $box = new Box();
  gift($box, "Hello");
  gift($box, "Goodbye");
  gift($box, 3);
  return $box->get();
}

function run(): void {
  var_dump(ts_generics_1());
  var_dump(ts_generics_2());
}

run();
    

Output

array(3) {
  [0]=>
  string(5) "Hello"
  [1]=>
  string(7) "Goodbye"
  [2]=>
  int(3)
}
array(3) {
  [0]=>
  string(5) "Hello"
  [1]=>
  string(7) "Goodbye"
  [2]=>
  int(3)
}

Enums

enum

 是常量,通常彼此相關(guān)的由一類型。與類常量等不同,enum是Hack類型系統(tǒng)中的一流類型。因此,它們可以用作原語或?qū)ο箢愋偷娜魏蔚胤降念愋妥⑨尅?/p>

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Enum;

enum Color: string {
  BLUE = "blue";
  RED = "red";
  GREEN = "green";
}

// Enums can be used as type annotations just like any other type.
function render_color(Color $c): void {
  echo $c;
}

render_color(Color::BLUE); // "blue"
render_color(Color::RED); // "red"

Output

bluered

可調(diào)用

有一個callable類型,但是Hack不允許它(HHVM接受它,但是如果你不關(guān)心類型檢查器錯誤)。

相反,Hack提供了一種更具表現(xiàn)力的可調(diào)用類型:

function(0..n parameter types): return type
<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Call;

function use_callable(
  Vector<int> $vec,
  (function(int) : ?int) $callback,
): Vector<?int> {
  $ret = Vector {};
  foreach ($vec as $item) {
    $ret[] = $callback($item);
  }
  return $ret;
}

function ts_callable(): void {
  $callable = function(int $i): ?int {
    return $i % 2 === 0 ? $i + 1 : null;
  };
  var_dump(use_callable(Vector {1, 2, 3}, $callable));
}

ts_callable();

// Returns
/*
object(HH\Vector)#3 (3) {
  [0]=>
  NULL
  [1]=>
  int(3)
  [2]=>
  NULL
}
*/

Output

object(HH\Vector)#3 (3) {
  [0]=>
  NULL
  [1]=>
  int(3)
  [2]=>
  NULL
}

元組(Tuples

元組提供指定可能不同類型的固定數(shù)量值的類型。元組最常見的用法是從函數(shù)返回多個值。

(type1,...,type n)

元組就像固定數(shù)組。您不能從元組中刪除或更改任何類型,但可以更改每種類型的值。要創(chuàng)建一個元組,您使用與數(shù)組相同的語法,但s / arraytuple

tuple(value1, ..., value n);
<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Tup;

// You don't use the keyword tuple when annotating with one
// You do use the keyword tuple when forming one.
function q_and_r(int $x, int $y): (int, int, bool) {
  return tuple(round($x / $y), $x % $y, $x % $y === 0);
}

function ts_tuple(): void {
  // Tuples lend themselves very well to list()
  list($q, $r, $has_remainder) = q_and_r(5, 2);
  var_dump($q);
  var_dump($r);
  var_dump($has_remainder);
}

ts_tuple();

Output

float(3)
int(1)
bool(false)

封面下的數(shù)組

在HHVM中,元組被實(shí)現(xiàn)為數(shù)組,您可以調(diào)用is_array()它們并獲取true返回值。

Type Aliases

Type aliases

 允許您為現(xiàn)有類型添加新名稱。它們可以像注釋中的現(xiàn)有類型一樣使用。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\TypeAlias;

type ID = int;
type Name = string;

class Customers {
  private array<ID, Name> $c;
  public function __construct() {
    $this->c = array();
    $this->c[0] = "Joel";
    $this->c[1] = "Fred";
    $this->c[2] = "Jez";
    $this->c[3] = "Tim";
    $this->c[4] = "Matthew";
  }

  public function get_name(ID $id): ?Name {
    if (!array_key_exists($id, $this->c)) {
      return null;
    }
    return $this->c[$id];
  }

  public function get_id(Name $name): ?ID {
    $key = array_search($name, $this->c);
    return $key ? $key : null;
  }
}

function ts_type_alias(): void {
  $c = new Customers();
  var_dump($c->get_name(0));
  var_dump($c->get_id("Fred"));
  var_dump($c->get_id("NoName"));
}

ts_type_alias();

Output

string(4) "Joel"
int(1)
NULL

Classname

Foo::class在PHP中是指包含完整限定名稱的字符串常量Foo。

Hack引入了一個特殊類別的別名classname<T>。所以,現(xiàn)在當(dāng)有人寫的時候Foo::class,Hack typechecker不僅能識別類的字符串表示,而且還提供了提供類本身的語義的新類型。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\CN;

<<__ConsistentConstruct>>
interface I {
  abstract const int A_CONST;
  public static function staticMeth(): void;
  public function meth(): void;
}

class C implements I {
  const int A_CONST = 10;
  public static function staticMeth(): void { echo "staticMeth\n"; }
  public function meth(): void { echo "meth\n"; }
  public function methOnlyInC(): void { echo "methOnlyInC\n"; }
}
class D {}

// With the classname<T> built-in type alias, the typechecker can now
// understand all these constructs!
function check_classname(classname<I> $cls, mixed $value): void {
  $const = $cls::A_CONST; // typechecked!
  $cls::staticMeth(); // typechecked!
  invariant($value instanceof $cls, "Bad if not");
  $value->meth(); // typechecked!
}

function ts_classname(): void {
  $c = new C();
  $d = new D();
  check_classname(C::class, $c);
  check_classname('C', $c); // error! only C::class is a classname
  check_classname(D::class, $d); // error! a D is not an I
}

ts_classname();

Output

staticMeth
meth

Fatal error: Class undefined: C in /data/users/joelm/user-documentation/guides/hack/20-types/02-type-system-examples/classname.php.type-errors on line 23

形狀

形狀是表示結(jié)構(gòu)化數(shù)組的特定類型別名,具有確定性名稱和鍵類型。它們也可以用作類型注釋。

<?hh

namespace Hack\UserDocumentation\Types\TypeSystem\Examples\Shp;

type customer = shape('id' => int, 'name' => string);

function create_user(int $id, string $name): customer {
  return shape('id' => $id, 'name' => $name);
}

function ts_shape(): void {
  $c = create_user(0, "James");
  var_dump($c['id']);
  var_dump($c['name']);
}

ts_shape();

Output

int(0)
string(5) "James"


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號