PHP类中用DocBlock模板约束返回类型时Psalm报错排查
解决Psalm对ServiceManager类getService方法的类型推断错误
问题出在两个地方:
- 类属性的
@template T和方法的@template T重复定义,导致Psalm没法正确关联传入的类名和返回实例的类型绑定 - 自定义的
hasService方法缺少类型断言注解,Psalm无法确认当该方法返回true时,$this->services[$service]的具体类型就是传入的目标类
修复方案
- 调整属性类型标注:移除属性上的
@template T,把数组类型改成array<class-string, object>——因为这个数组本来就是用来存任意类的实例,不需要单一模板约束。 - 明确方法的模板约束:在
getService方法里用@template T of object限定T必须是对象类型,让class-string<T>参数和T|null返回值形成强绑定。 - 给hasService添加断言注解:如果要用自定义的
hasService做判断,必须给它加上@psalm-assert-if-true T $this->services[$service],让Psalm能推断出条件成立时数组元素的类型就是T。
修改后的完整代码
class ServiceManager { /** * @var array<class-string, object> */ private array $services = []; /** * @template T of object * @psalm-param class-string<T> $service * @psalm-assert-if-true T $this->services[$service] */ public function hasService(string $service): bool { return isset($this->services[$service]); } /** * @template T of object * @psalm-param class-string<T> $service * @return T|null */ public function getService(string $service): ?object { if ($this->hasService($service)) { return $this->services[$service]; } return null; } }
简化版(直接用isset判断)
如果不需要单独的hasService方法,也可以直接用空合并运算符简化代码,Psalm同样能正确推断类型:
class ServiceManager { /** * @var array<class-string, object> */ private array $services = []; /** * @template T of object * @psalm-param class-string<T> $service * @return T|null */ public function getService(string $service): ?object { return $this->services[$service] ?? null; } }
这样修改后,Psalm就能准确识别传入的类名字符串和返回实例的类型一致性,不会再抛出类型错误。
内容的提问来源于stack exchange,提问作者Gashmob
相关产品推荐
相关产品推荐

