You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP类中用DocBlock模板约束返回类型时Psalm报错排查

解决Psalm对ServiceManager类getService方法的类型推断错误

问题出在两个地方:

  • 类属性的@template T和方法的@template T重复定义,导致Psalm没法正确关联传入的类名和返回实例的类型绑定
  • 自定义的hasService方法缺少类型断言注解,Psalm无法确认当该方法返回true时,$this->services[$service]的具体类型就是传入的目标类

修复方案

  1. 调整属性类型标注:移除属性上的@template T,把数组类型改成array<class-string, object>——因为这个数组本来就是用来存任意类的实例,不需要单一模板约束。
  2. 明确方法的模板约束:在getService方法里用@template T of object限定T必须是对象类型,让class-string<T>参数和T|null返回值形成强绑定。
  3. 给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 17:17:52