簡體   English   中英

如何讓 phpstan 推斷我的 Laravel 收集管道的類型?

[英]How to get phpstan to infer the type for my Laravel Collection pipeline?

鑒於我的 class

<?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 不能推斷出這個 pipe 的正確結果類型? 如何讓 phpstan 理解 pipe?


我可以驗證我的代碼在 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只是一個虛擬的 class 。 唯一相關的是它需要一個stdClass實例並將其轉換為?Foo實例。

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

Illuminate\Support\Collection class 本身不是通用的。 所以寫Collection<Foo>是錯誤的。 這會導致錯誤消息,如Illuminate\Support\Collection&iterable<Illuminate\Support\Collection&iterable<stdClass>>

你有兩個選擇:

  1. 安裝拉拉斯坦 這是 Laravel 的 PHPStan 擴展。 它具有使Illuminate\Support\Collection class 通用的存根文件

  2. 或者,如果您只是使用獨立illuminate/collections而不使用完整的 Laravel 應用程序,您可以編寫自己的存根文件。 來自PHPStan 文檔

...您可以使用正確的 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) {}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM