如何为返回Token数组的PHP Generator添加正确类型提示?
解决Doctrine Lexer Token生成器的类型提示问题
问题背景
我有一个返回Generator的数据提供者,每个生成元素是包含字符串和Token数组的数组,代码如下:
public static function dataProvider(): Generator { yield ['15', [new Token(15, Lexer::T_INTEGER, 0)]]; // 更多类似项... }
尝试为该方法添加类型提示:
/** * @return Generator<array{string, Token<int, int|string>[]}> */
遇到的PhpStan错误
Generator expects value type array{string, array<Doctrine\Common\Lexer\Token<int, int|string>>}, array{'15', array{Doctrine\Common\Lexer\Token<int, int>} } given. 💡 Offset 1 (array<Doctrine\Common\Lexer\Token<int, int|string>>) does not accept type array{Doctrine\Common\Lexer\Token<int, int>}: Template type V on class Doctrine\Common\Lexer\Token is not covariant.
使用@return Generator<array{string, Token[]}>可消除报错,但会触发“未指定Token的泛型类型T、V”的提示。
Token类定义(来自doctrine/lexer)
/** * @template T of UnitEnum|string|int * @template V of string|int * @implements ArrayAccess<string,mixed> */ final class Token implements ArrayAccess
解决方案
问题核心在于Token的模板参数V并非协变类型,无法用更宽泛的int|string去接收更具体的int类型。以下是两种可行的解决方式:
方式一:精确匹配实际泛型类型
如果所有生成的Token实例中,V的类型统一为int,直接将类型提示写精确:
/** * @return Generator<array{string, Token<int, int>[]}> */ public static function dataProvider(): Generator { yield ['15', [new Token(15, Lexer::T_INTEGER, 0)]]; // 更多类似项... }
方式二:兼容多类型的Token数组
如果数据提供者中同时存在V为int和string的Token,可以用联合类型明确声明数组元素的类型:
/** * @return Generator<array{string, list<Token<int, int>|Token<int, string>>}> */ public static function dataProvider(): Generator { yield ['15', [new Token(15, Lexer::T_INTEGER, 0)]]; yield ['foo', [new Token('foo', Lexer::T_STRING, 0)]]; }
这里用list代替array更贴合实际的索引数组场景,也能让PhpStan做更严格的类型检查。
内容的提问来源于stack exchange,提问作者Alexandre Tranchant
相关产品推荐
相关产品推荐

