如何在PHP/PHPStan中返回带泛型提示的通用接口?
PHP/PHPStan泛型类型提示:返回带泛型的接口对象并限制方法调用
需求明确:
- 调用创建函数后,返回的接口对象的
build()方法必须返回具体的Car类型 - 不能调用实现类(如
CarBuilder)特有的notAnInterfaceFunc()方法
现有代码示例:
class Car { } /** * @template S of object */ interface BuilderInterface { /** * @return S */ public function build(): object; } /** * @implements BuilderInterface<Car> */ class CarBuilder implements BuilderInterface { public function build(): object { return new Car(); } public function notAnInterfaceFunc(): void { } } /** * @template T of BuilderInterface * @param class-string<T> $builderClass * @return T */ function createBuilder(string $builderClass): BuilderInterface { return new $builderClass(); } /** * @template T of BuilderInterface * @param class-string<T> $builderClass * @return BuilderInterface */ function createBuilder1(string $builderClass): BuilderInterface { return new $builderClass(); } // 现有问题: $builder = createBuilder(CarBuilder::class); \PHPStan\dumpType($builder->build()); // 正确返回Car,但能调用notAnInterfaceFunc() $builder->notAnInterfaceFunc(); // 无报错,不符合需求 $builder1 = createBuilder1(CarBuilder::class); \PHPStan\dumpType($builder1->build()); // 仅返回object,类型丢失,不符合需求
问题分析
createBuilder:返回泛型参数T(即实现类本身),因此能调用实现类的所有方法,但不符合“仅暴露接口方法”的要求createBuilder1:返回无泛型的BuilderInterface,泛型类型信息丢失,导致build()无法推断出具体的Car类型
解决方案
调整函数的泛型定义,同时约束实现类的泛型参数,让函数返回带有具体泛型参数的BuilderInterface:
/** * @template S of object * @param class-string<BuilderInterface<S>> $builderClass * @return BuilderInterface<S> */ function createBuilderCorrect(string $builderClass): BuilderInterface { return new $builderClass(); }
修改后验证
$builderCorrect = createBuilderCorrect(CarBuilder::class); \PHPStan\dumpType($builderCorrect->build()); // 输出:Car(正确) $builderCorrect->notAnInterfaceFunc(); // PHPStan报错:Method notAnInterfaceFunc() does not exist on BuilderInterface<Car>(符合需求)
完整修改后代码
class Car { } /** * @template S of object */ interface BuilderInterface { /** * @return S */ public function build(): object; } /** * @implements BuilderInterface<Car> */ class CarBuilder implements BuilderInterface { public function build(): object { return new Car(); } public function notAnInterfaceFunc(): void { } } /** * @template S of object * @param class-string<BuilderInterface<S>> $builderClass * @return BuilderInterface<S> */ function createBuilderCorrect(string $builderClass): BuilderInterface { return new $builderClass(); } // 验证效果 $builderCorrect = createBuilderCorrect(CarBuilder::class); \PHPStan\dumpType($builderCorrect->build()); // $builderCorrect->notAnInterfaceFunc(); // 取消注释会触发PHPStan错误
内容的提问来源于stack exchange,提问作者dr0bz
相关产品推荐
相关产品推荐

