W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
您可能已經(jīng)注意到,并非所有內(nèi)容都被注釋(例如局部變量)。然而,類型檢查器仍然能夠?qū)︻愋筒黄ヅ渥龀龊侠淼臄嘌?。它通過(guò)類型推斷填充注釋空白。
基本類型推理是基于給定的變量的已知類型來(lái)推斷變量的類型。而類型檢查器可以根據(jù)它所看到的注釋以及程序的當(dāng)前流程來(lái)做推理。
局部變量沒(méi)有注釋類型。它們的類型是根據(jù)程序的流程推斷的。實(shí)際上,您可以將不同類型的不同值分配給局部變量。當(dāng)從函數(shù)或方法返回一個(gè)局部變量時(shí),或者當(dāng)它被比較或以其他方式與已知類型的變量進(jìn)行比較時(shí),是唯一重要的是本地變量的類型。
<?hh
namespace Hack\UserDocumentation\Types\Inference\Examples\LocalVariables;
function foo(): int {
$a = str_shuffle("ABCDEF"); // $a is a string
if (strpos($a, "A") === false) {
$a = 4; // $a is an int
} else {
$a = 2; // $a is an int
}
// Based on the flow of the program, $a is guaranteed to be an int at this
// point, so it is safe to return as an int.
return $a;
}
function run(): void {
var_dump(foo());
}
run();
Output
int(2)
上面的例子顯示了一個(gè)變量被分配給int兩個(gè)分支的情況if/else。這使得類型檢查器很容易確定變量可以而且只有int當(dāng)它遇到該變量時(shí)return。
但是,如果不是將變量分配給條件的兩個(gè)分支中的相同類型,那么會(huì)決定在每個(gè)分支中將其分配給其他類型?
<?hh
namespace Hack\UserDocumentation\Types\Inference\Examples\Unresolved;
function foo(): arraykey {
$a = str_shuffle("ABCDEF"); // $a is a string
if (strpos($a, "A") === false) {
$a = 4; // $a is an int
} else {
$a = "Hello"; // $a is string
}
// Based on the flow of the program, at this point $a is either an int or
// string. You have an unresolved type; or, to look at it another way, you
// the union of an int and string. So you can only perform operations that
// can be performed on both of those types.
var_dump($a + 20); // Nope. This isn't good for a string
$arr = array();
$arr[$a] = 4; // Fine. Since an array key can be an int or string
// arraykey is fine since it is either an int or string
return $a;
}
var_dump(foo());
Output
int(20)
string(5) "Hello"
在條件分支中,我們將相同的局部變量分配給兩種類型之一。這使得局部變量未解決,這意味著typechecker知道變量可以是兩種類型之一,但不知道哪一種。所以在這一點(diǎn)上,只允許在兩種類型上執(zhí)行的操作。
通常,類屬性被注釋,所以類型檢查器最初知道它們的預(yù)期類型。但有時(shí)候,類型檢查器必須做出一些假設(shè),這使得推斷進(jìn)一步使用屬性比本地變量復(fù)雜得多。
<?hh
namespace Hack\UserDocumentation\Types\Inference\Examples\Props;
class A {
protected ?int $x;
public function __construct() {
$this->x = 3;
}
public function setPropToNull(): void {
$this->x = null;
}
public function checkPropBad(): void {
// Typechecker knows $x isn't null after this validates
if ($this->x !== null) {
// We know that this doesn't call A::setPropToNull(), but the typechecker
// does not since inferences is local to the function.
// Commenting out so typechecker passes on all examples
does_not_set_to_null();
// We know that $x is still not null, but the typechecker doesn't
take_an_int($this->x);
}
}
public function checkPropGood(): void {
// Typechecker knows $x isn't null after this validates
if ($this->x !== null) {
// We know that this doesn't call A::setPropToNull(), but the typechecker
// does not since inferences is local to the function.
does_not_set_to_null();
// Use this invariant to tell the typechecker what's happening.
invariant($this->x !== null, "We know it is not null");
// We know that $x is still not null, and now the typechecker does too
// Could also have used a local variable here saying:
// $local = $this->x;
// takes_an_int($local);
take_an_int($this->x);
}
}
}
function does_not_set_to_null(): void {
echo "I don't set A::x to null" . PHP_EOL;
}
function take_an_int(int $x): void {
var_dump($x);
}
function run(): void {
$a = new A();
$a->checkPropBad();
$a->checkPropGood();
}
run();
Output
I don't set A::x to null
int(3)
I don't set A::x to null
int(3)
類型檢查器僅將本機(jī)推送到功能。例如,如果一個(gè)函數(shù)調(diào)用另一個(gè)函數(shù),它不會(huì)對(duì)函數(shù)外部可能發(fā)生什么的假設(shè)。這就是為什么typechecker會(huì)拋出一個(gè)錯(cuò)誤,即使我們知道眼睛測(cè)試,沒(méi)有null問(wèn)題。
通過(guò)使用設(shè)置為屬性值的局部變量或通過(guò)使用來(lái)解決此問(wèn)題invariant()。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: