Refining

2018-10-04 12:30 更新

Refining類型基本上確定一種類型的值也是另一種類型。

假設(shè)您想要使用給定類型并將該特定類型細(xì)化為另一種兼容類型。Hack允許通過(guò)在控制流情況下使用三個(gè)構(gòu)造:

  • 檢查 null
  • 類型查詢(例如,通過(guò)is_float()
  • 運(yùn)用 instanceof

不可否認(rèn)的

請(qǐng)記住,可空類型允許變量的值為其類型或null。有時(shí)您只想使用null該類型的非部分內(nèi)容。您可以使用空檢查來(lái)細(xì)化可空類型。

<?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)

混合為原始

請(qǐng)記住,它mixed代表任何可注釋類型(可空類型除外)。mixed可以通過(guò)使用內(nèi)置類型的查詢功能來(lái)將其細(xì)化為更具體的原始類型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)

對(duì)象實(shí)例檢查

有時(shí)你想知道一個(gè)對(duì)象是一個(gè)父類的子對(duì)象還是實(shí)現(xiàn)一個(gè)特定的接口。您可以使用instanceof支票來(lái)幫助做出這一決定。

<?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

看看這個(gè)例子。

<?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()是在通過(guò)了Base,和所有兒童Base實(shí)行foo(),一旦instanceof支票上完成$b,并檢查返回true時(shí),typechecker必須假設(shè)$b現(xiàn)在是一個(gè)未解決的類型兩者的BaseI。而且由于不是所有的執(zhí)行者I都必須處于層次結(jié)構(gòu)中Base,我們不能保證foo()不再有可用性。

Invariant

還有一個(gè)特殊功能:

invariant(<bool expression of fact>, "Message if not")

可以在外部控制流情況下使用。它本質(zhì)上是對(duì)類型檢查器的一個(gè)斷言,即你在布爾語(yǔ)句中聲明的是事實(shí)和真實(shí)的。

在上述任何精煉方案中,可以使用invariant()與條件檢查相反的方法。

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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)