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

Psalm抽象类及其实现的类型推断疑问:模板类型不匹配

问题与解答

问题背景

以下PHP代码使用Psalm进行静态类型检查时,出现如下错误:

ERROR: InvalidArgument - 52:14 - Argument 1 of someFunction expects Pet<Cat|Dog>, but Cat|Dog provided

相关代码:

<?php

/**
 * @template T
 */
abstract class Animal
{
}

/**
 * @template T of Cat|Dog
 * @extends Animal<T> 
 */
abstract class Pet extends Animal
{
    abstract public function say(): string;
}

/**
 * @extends Pet<Cat> 
 */
class Cat extends Pet
{
    public function say(): string
    {
        return 'meow';
    }
}

/**
 * @extends Pet<Dog> 
 */
class Dog extends Pet
{
    public function say(): string
    {
        return 'woof';
    }
}

function someFunction(Pet $pet): void
{
    echo $pet->say();
}

$pet = rand(0,1) === 0 
    ? new Dog()
    : new Cat()
;
someFunction($pet);

需要解决的疑问:

  1. 代码中的泛型模板定义是否存在问题?
  2. 为何Psalm不认为Pet<Cat|Dog>与Cat|Dog是相同类型?
  3. 除基线或抑制错误外,是否有其他解决办法?

解答

1. 模板定义是否存在问题?

原代码的泛型模板定义本身是正确的:

  • Animal类定义了基础模板T
  • Pet类继承Animal并约束模板T为Cat|Dog
  • Cat和Dog类分别继承Pet并绑定具体的模板参数,符合泛型继承的规则

2. 类型不匹配的原因

Psalm的类型系统严格区分两种不同的类型:

  • Pet<Cat|Dog>:指单个Pet实例,其泛型参数是Cat与Dog的联合类型(即该实例的模板参数同时兼容两种类型)
  • Cat|Dog:指Cat实例或Dog实例的联合类型,而Cat本质是Pet<Cat>的子类,Dog是Pet<Dog>的子类,因此这个联合类型等价于Pet<Cat>|Pet<Dog>

这两种类型并非同一概念:前者是单一泛型类的联合参数实例,后者是两个不同泛型实例的联合,因此Psalm判定类型不匹配。

3. 可行的解决办法

办法一:显式声明函数参数接受联合泛型类型

修改someFunction的类型注解,明确接受Pet<Cat>或Pet<Dog>的实例:

/**
 * @param Pet<Cat>|Pet<Dog> $pet
 */
function someFunction(Pet $pet): void
{
    echo $pet->say();
}

这样Psalm就能识别Cat|Dog的联合类型符合参数要求。

办法二:显式标注变量类型为Pet

在$pet赋值前添加类型声明,强制将变量类型视为Pet(兼容所有符合Pet泛型约束的实例):

/** @var Pet $pet */
$pet = rand(0,1) === 0 
    ? new Dog()
    : new Cat()
;

此方式会让Psalm忽略具体的泛型参数差异,匹配函数的参数类型。

办法三:调整函数参数的类型声明

直接将函数参数类型改为Pet<Cat>|Pet<Dog>,替代原有的Pet:

function someFunction(Pet<Cat>|Pet<Dog> $pet): void
{
    echo $pet->say();
}

这种方式最直接,明确告知Psalm参数接受的具体类型范围。


内容的提问来源于stack exchange,提问作者Storm

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 08:30:30