返回PHPStan数组中的对象模型



假设你有3个这样的模型

class User extends AbstractModel {
protected string $name = 'User';
} 
class Car extends AbstractModel {
protected int $weels = 4;
} 
class House extends AbstractModel {
protected string $address = 'Some Street 26a';
} 

那么你有一个函数返回3个模型,像这样

protected function generateModels(): array
{
$user  = new User();
$car   = new Car();
$house = new House();
return [$user, $car, $house]
}

那么你有一些像这样的测试

/**
* @test
*/
public fuction this_is_some_random_test(): void
{
[
$user,
$car,
$house,
] = $this->generateModels();
$user->name;
$address->weels;
$house->address;
$result1 = some_function_require_user_model($user);
//some assertions
}

那么你需要如何输入提示函数generateModels(), PHPstan理解它可以是多个模型?因为array<int, mixed>不起作用,array<int, AbstractModel>也不起作用,因为它会像property $name doesnt exist on AbstractModel一样抱怨,第三个像array<int, User|Car|House>一样似乎也不起作用,因为你会得到同样的错误,然后函数说Car|House类型现在是允许的。那么我怎样才能正确地输入hint呢?

PHPStan level 9

当一个方法返回array<int,User|Car|House>时,这意味着该方法返回这些类的实例数组。根据该注释,您可以获得[User,Car][Car,House]或仅[Car,Car,Car](或任何其他组合)。这就是PHPStan抱怨这个的原因:它试图告诉你将来可能会有错误。虽然您(作为作者)知道此时第一个位置包含User,但代码不知道它,因此它假设第一个位置可能不是User,而是CarHouse,它们不像User那样包含属性$name。为了避免这种可能的错误,您可以使用instanceof来检查类。如果您使用$res[0] instanceof User, PHPStan(和代码本身)将知道数组中的第一项是User的实例,这意味着您可以毫无顾虑地获得$name属性。

$res = $this->generateModels(); // $res is array<User|Car|House>
$item1 = $res[0]; // $item1 is User|Car|House
echo $item1->name; // Error: name has only User (but $item1 is User, Car or House)
if ($item1 instanceof User) {
echo $item1->name; // OK: The code knows $item1 is 100% User
} 

但是如果你知道数组内部类型的顺序(例如,第一个是User,第二个是Car,最后是House),你可以使用数组形状用于指定单个键的类型。

class ModelGenerator
{
/**
* @return array{0: User, 1: Car, 2: House}
*/
protected function generateModels(): array
{
$user  = new User();
$car   = new Car();
$house = new House();

return [$user, $car, $house]
}
}

最新更新