Skip to content

Commit 62a0dab

Browse files
committed
[TypeInfo] Add ArrayShapeType::$sealed
1 parent d768193 commit 62a0dab

File tree

6 files changed

+28
-6
lines changed

6 files changed

+28
-6
lines changed

src/Symfony/Component/TypeInfo/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CHANGELOG
1111
* Add type alias support in `TypeContext` and `StringTypeResolver`
1212
* Add `CollectionType::mergeCollectionValueTypes()` method
1313
* Add `ArrayShapeType` to represent the exact shape of an array
14+
* Add `ArrayShapeType::$sealed` to represent array shape with extra keys
1415

1516
7.2
1617
---

src/Symfony/Component/TypeInfo/Tests/Type/ArrayShapeTypeTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ public function testAccepts()
7676

7777
$this->assertTrue($type->accepts(['foo' => true]));
7878
$this->assertTrue($type->accepts(['foo' => true, 'bar' => 'string']));
79+
80+
$type = new ArrayShapeType(['foo' => ['type' => Type::bool()]], sealed: false);
81+
82+
$this->assertTrue($type->accepts(['foo' => true, 'other' => 'string']));
7983
}
8084

8185
public function testToString()
@@ -94,5 +98,10 @@ public function testToString()
9498
'bar' => ['type' => Type::string(), 'optional' => true],
9599
]);
96100
$this->assertSame("array{'bar'?: string, 'foo': bool}", (string) $type);
101+
102+
$type = new ArrayShapeType([
103+
'foo' => ['type' => Type::bool()],
104+
], sealed: false);
105+
$this->assertSame("array{'foo': bool, ...}", (string) $type);
97106
}
98107
}

src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public static function resolveDataProvider(): iterable
7676
// array shape
7777
yield [Type::arrayShape(['foo' => Type::true(), 1 => Type::false()]), 'array{foo: true, 1: false}'];
7878
yield [Type::arrayShape(['foo' => ['type' => Type::bool(), 'optional' => true]]), 'array{foo?: bool}'];
79+
yield [Type::arrayShape(['foo' => Type::bool()], sealed: false), 'array{foo: bool, ...}'];
7980

8081
// object shape
8182
yield [Type::object(), 'object{foo: true, bar: false}'];

src/Symfony/Component/TypeInfo/Type/ArrayShapeType.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ final class ArrayShapeType extends CollectionType
3131
/**
3232
* @param array<array{type: Type, optional: bool}> $shape
3333
*/
34-
public function __construct(array $shape)
35-
{
34+
public function __construct(
35+
array $shape,
36+
private readonly bool $sealed = true,
37+
) {
3638
$keyTypes = [];
3739
$valueTypes = [];
3840

@@ -66,6 +68,11 @@ public function getShape(): array
6668
return $this->shape;
6769
}
6870

71+
public function isSealed(): bool
72+
{
73+
return $this->sealed;
74+
}
75+
6976
public function accepts(mixed $value): bool
7077
{
7178
if (!\is_array($value)) {
@@ -81,7 +88,7 @@ public function accepts(mixed $value): bool
8188
foreach ($value as $key => $itemValue) {
8289
$valueType = $this->shape[$key]['type'] ?? false;
8390
if (!$valueType) {
84-
return false;
91+
return !$this->sealed;
8592
}
8693

8794
if (!$valueType->accepts($itemValue)) {
@@ -105,6 +112,10 @@ public function __toString(): string
105112
$items[] = \sprintf('%s: %s', $itemKey, $value['type']);
106113
}
107114

115+
if (!$this->sealed) {
116+
$items[] = '...';
117+
}
118+
108119
return \sprintf('array{%s}', implode(', ', $items));
109120
}
110121
}

src/Symfony/Component/TypeInfo/TypeFactoryTrait.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,13 @@ public static function dict(?Type $value = null): CollectionType
198198
/**
199199
* @param array<array{type: Type, optional?: bool}|Type> $shape
200200
*/
201-
public static function arrayShape(array $shape): ArrayShapeType
201+
public static function arrayShape(array $shape, bool $sealed = true): ArrayShapeType
202202
{
203203
return new ArrayShapeType(array_map(static function (array|Type $item): array {
204204
return $item instanceof Type
205205
? ['type' => $item, 'optional' => false]
206206
: ['type' => $item['type'], 'optional' => $item['optional'] ?? false];
207-
}, $shape));
207+
}, $shape), $sealed);
208208
}
209209

210210
/**

src/Symfony/Component/TypeInfo/TypeResolver/StringTypeResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Typ
110110
];
111111
}
112112

113-
return Type::arrayShape($shape);
113+
return Type::arrayShape($shape, $node->sealed);
114114
}
115115

116116
if ($node instanceof ObjectShapeNode) {

0 commit comments

Comments
 (0)