Skip to content

Instantly share code, notes, and snippets.

@ruudk
Last active August 20, 2025 17:31
Show Gist options
  • Save ruudk/efcb19b114b8254d1733038cddc9edf6 to your computer and use it in GitHub Desktop.
Save ruudk/efcb19b114b8254d1733038cddc9edf6 to your computer and use it in GitHub Desktop.
Generics issues with PHPStorm
<?php
declare(strict_types=1);
// composer require webonyx/graphql-php
use GraphQL\Language\AST\DirectiveNode;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\NodeList;
include __DIR__ . '/vendor/autoload.php';
/**
* @var NodeList<DirectiveNode> $list
*/
$list = new NodeList([
new DirectiveNode([])
]);
foreach ($list as $item) {
// this works, PHPStorm understands $item is DirectiveNode
echo $item::class . PHP_EOL;
}
// But now we do this:
$fieldNode = new FieldNode([
'directives' => new NodeList([
new DirectiveNode([])
])
]);
foreach ($fieldNode->directives as $item) {
// PHPStorm thinks $item is mixed
echo $item::class . PHP_EOL;
}
<?php
declare(strict_types=1);
include __DIR__ . '/vendor/autoload.php';
final class ChunkedIterator
{
/**
* @template TKey of array-key
* @template TValue of mixed
*
* @param Iterator<TKey, TValue> $iterable
*
* @return Generator<int, non-empty-array<TKey, TValue>>
*/
public static function chunk(Iterator $iterable, int $chunkSize) : Generator
{
$chunk = [];
for ($i = 0; $iterable->valid(); ++$i) {
$chunk[$iterable->key()] = $iterable->current();
if (count($chunk) === $chunkSize) {
yield $chunk;
$chunk = [];
}
$iterable->next();
}
if ($chunk !== []) {
yield $chunk;
}
}
}
/**
* @return Generator<int, array{id: int}>
*/
function generator() : Generator
{
for ($i = 0; $i < 13; ++$i) {
yield ['id' => $i];
}
}
$iterator = ChunkedIterator::chunk(generator(), 3);
// \PHPStan\dumpType($iterator);
// Dumped type: Generator<int, non-empty-array<int, array{id: int}>, mixed, mixed>
foreach ($iterator as $chunk) {
// \PHPStan\dumpType($chunk);
// Dumped type: non-empty-array<int, array{id: int}>
// But PHPStorm thinks $chunk is $2|array|mixed
foreach ($chunk as $item) {
// \PHPStan\dumpType($item);
// Dumped type: array{id: int}
// But PHPStorm thinks $item is array|Generator
echo $item['id'] . PHP_EOL;
}
echo "Flush...\n";
}
<?php
declare(strict_types=1);
include __DIR__ . '/vendor/autoload.php';
// composer require symfony/type-info
use Symfony\Component\TypeInfo\Type;
/**
* @template T of Type = Type
*/
interface TypeInitializer
{
/**
* @param T $type
*/
public function initialize(Type $type) : void;
}
/**
* @implements TypeInitializer<Type\ObjectType<*>>
*/
final class ObjectTypeInitializer implements TypeInitializer
{
#[Override]
public function initialize(Type $type) : void
{
// \PHPStan\dumpType($type);
// Dumped type: Symfony\Component\TypeInfo\Type\ObjectType<*>
// PHPStorm thinks $type is just a Type
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment