在Hack中描述類型主要通過Hack源代碼中的顯式注釋來完成。Hack有很多可能的注釋類型。您可以在我們的表格中查看每種類型的摘要。
PHP中可用的主要基本類型可在Hack中作為顯式類型注釋使用。這些包括:
<?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"
Hack 不支持Alias primitives。因此,以下不是要在類型注釋中使用的有效類型:
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
Awaitable<void>
。這意味著雖然功能本身正在等待返回,但是等待的結(jié)果將沒有價值。這實(shí)際上意味著異步函數(shù)做了一些不需要調(diào)用者返回值的異步操作。
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
注意:
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本質(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是允許在類本身或其子類的實(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是特殊的聯(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是特殊的聯(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接口: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類型由?
放置為類型本身的前綴(例如,?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)
}
是常量,通常彼此相關(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
有一個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
}
元組提供指定可能不同類型的固定數(shù)量值的類型。元組最常見的用法是從函數(shù)返回多個值。
(type1,...,type n)
元組就像固定數(shù)組。您不能從元組中刪除或更改任何類型,但可以更改每種類型的值。要創(chuàng)建一個元組,您使用與數(shù)組相同的語法,但s / array
/ tuple
。
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)
在HHVM中,元組被實(shí)現(xiàn)為數(shù)組,您可以調(diào)用is_array()它們并獲取true返回值。
允許您為現(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
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"
更多建議: