From 7d6c1b11b4923ccb94a12ecad38844af156dcd7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Tue, 18 Nov 2025 11:36:38 +0400 Subject: [PATCH] Fix handling of array shapes without keys --- .gitignore | 1 + src/PseudoTypes/ShapeItem.php | 2 +- src/TypeResolver.php | 2 +- tests/unit/PseudoTypes/ArrayShapeTest.php | 39 +++++++++++++++++++++++ tests/unit/TypeResolverTest.php | 7 ++++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 698d0c3..0b2ba50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # IDE Shizzle; it is recommended to use a global .gitignore for this but since this is an OSS project we want to make # it easy to contribute .idea +.vscode /nbproject/private/ .buildpath .project diff --git a/src/PseudoTypes/ShapeItem.php b/src/PseudoTypes/ShapeItem.php index cb86b92..10a9a89 100644 --- a/src/PseudoTypes/ShapeItem.php +++ b/src/PseudoTypes/ShapeItem.php @@ -42,7 +42,7 @@ public function isOptional(): bool public function __toString(): string { - if ($this->key !== null) { + if ($this->key !== null && $this->key !== '') { return sprintf( '%s%s: %s', $this->key, diff --git a/src/TypeResolver.php b/src/TypeResolver.php index af57226..6ebca5e 100644 --- a/src/TypeResolver.php +++ b/src/TypeResolver.php @@ -255,7 +255,7 @@ public function createType(?TypeNode $type, Context $context): Type ...array_map( function (ArrayShapeItemNode $item) use ($context): ArrayShapeItem { return new ArrayShapeItem( - (string) $item->keyName, + $item->keyName !== null ? (string) $item->keyName : null, $this->createType($item->valueType, $context), $item->optional ); diff --git a/tests/unit/PseudoTypes/ArrayShapeTest.php b/tests/unit/PseudoTypes/ArrayShapeTest.php index 0069a68..981a98b 100644 --- a/tests/unit/PseudoTypes/ArrayShapeTest.php +++ b/tests/unit/PseudoTypes/ArrayShapeTest.php @@ -23,4 +23,43 @@ public function testExposeItems(): void $this->assertSame([$item1, $item2], $arrayShape->getItems()); } + + /** + * @dataProvider provideToStringData + * @covers ::__toString + */ + public function testToString(string $expectedResult, ArrayShape $arrayShape): void + { + $this->assertSame($expectedResult, (string) $arrayShape); + } + + /** + * @return array + */ + public static function provideToStringData(): array + { + return [ + 'with keys' => [ + 'array{foo: true, bar?: false}', + new ArrayShape( + new ArrayShapeItem('foo', new True_(), false), + new ArrayShapeItem('bar', new False_(), true) + ), + ], + 'with empty keys' => [ + 'array{true, false}', + new ArrayShape( + new ArrayShapeItem('', new True_(), false), + new ArrayShapeItem('', new False_(), false) + ), + ], + 'without keys' => [ + 'array{true, false}', + new ArrayShape( + new ArrayShapeItem(null, new True_(), false), + new ArrayShapeItem(null, new False_(), false) + ), + ], + ]; + } } diff --git a/tests/unit/TypeResolverTest.php b/tests/unit/TypeResolverTest.php index 915471d..d77c34f 100644 --- a/tests/unit/TypeResolverTest.php +++ b/tests/unit/TypeResolverTest.php @@ -1150,6 +1150,13 @@ public function shapeStructures(): array new ArrayShapeItem('bar', new Integer(), false) ), ], + [ + 'array{string, int}', + new ArrayShape( + new ArrayShapeItem(null, new String_(), false), + new ArrayShapeItem(null, new Integer(), false) + ), + ], [ 'array{foo?: string, bar: int}', new ArrayShape(