微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何让 phpstan 推断我的 Laravel Collection 管道的类型?

如何解决如何让 phpstan 推断我的 Laravel Collection 管道的类型?

给我的班级

<?PHP
declare(strict_types=1);

use Illuminate\Support\Collection;
use stdClass;

class PHPstanIssue
{
    /**
     * @param Collection<Collection<stdClass>> $collection
     *
     * @return Collection<Foo>
     */
    public function whyDoesThisFail(Collection $collection): Collection
    {
        return $collection
            ->flatten() // Collection<stdClass>
            ->map(static function (\stdClass $std): ?Foo {
                return Foo::get($std);
            }) // should Now be Collection<?Foo>
            ->filter(); // should Now be Collection<Foo>
    }
}

我很困惑为什么 PHPstan (0.12.64) 会失败:

18: [ERROR] Method PHPstanIssue::whyDoesThisFail() should return
Illuminate\Support\Collection&iterable<Foo> but returns 
Illuminate\Support\Collection&iterable<Illuminate\Support\Collection&iterable<stdClass>>. (PHPstan)

为什么 PHPstan 不能推断出这个管道的正确结果类型?如何让 PHPstan 理解管道?


我可以验证我的代码PHPunit 测试用例中是否有效:

class MyCodeWorks extends TestCase
{
    public function testPipeline()
    {
        $result = (new PHPstanIssue())->whyDoesThisFail(
            new Collection(
                [
                    new Collection([new \stdClass(),new \stdClass()]),new Collection([new \stdClass()]),]
            )
        );

        self::assertCount(3,$result);
        foreach ($result as $item) {
            self::assertInstanceOf(Foo::class,$item);
        }
    }
}

会过去的。


就这个问题而言,我的 Foo 只是一个虚拟班级。重要的是它需要一个 stdClass 实例并将其转换为一个 ?Foo 实例。

class Foo
{
    public static function get(\stdClass $std): ?Foo
    {
        // @PHPstan-ignore-next-line
        return (bool) $std ? new static() : null;
    }
}

解决方法

Illuminate\Support\Collection 类本身不是通用的。所以写 Collection<Foo> 是错误的。这会导致 Illuminate\Support\Collection&iterable<Illuminate\Support\Collection&iterable<stdClass>>

之类的错误消息

您有两个选择:

  1. 安装 Larastan。它是 Laravel 的 PHPStan 扩展。它有 stub files 使 Illuminate\Support\Collection 类通用。

  2. 或者,如果您只是使用 illuminate/collections 独立包而没有完整的 Laravel 应用程序,您可以编写自己的存根文件。 来自PHPStan docs

...您可以使用正确的 PHPDoc 编写存根文件。它就像源代码,但 PHPStan 只从中读取 PHPDocs。所以命名空间和类/接口/特征/方法/函数名称必须与您描述的原始来源匹配。但是方法体可以留空,PHPStan 只对 PHPDocs 感兴趣。

对于您的示例,以下存根文件应该足够了:

<?php

namespace Illuminate\Support;

/**
 * @template TKey
 * @template TValue
 * @implements \ArrayAccess<TKey,TValue>
 * @implements Enumerable<TKey,TValue>
 */
class Collection implements \ArrayAccess,Enumerable
{
    /**
     * @template TReturn
     * @param callable(TValue,TKey): TReturn $callable
     * @return static<TKey,TReturn>
     */
    public function map($callable) {}
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?