mirror of
https://github.com/danog/Valinor.git
synced 2024-11-30 04:39:05 +01:00
feat: introduce composite types
Composite types are composed of other types and must now implement a method to recursively traverse all sub-types.
This commit is contained in:
parent
5f9d41cf35
commit
892f3831c2
@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type;
|
||||
|
||||
/** @api */
|
||||
interface CombiningType extends Type
|
||||
interface CombiningType extends CompositeType
|
||||
{
|
||||
public function isMatchedBy(Type $other): bool;
|
||||
|
||||
|
@ -7,7 +7,7 @@ namespace CuyZ\Valinor\Type;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
|
||||
/** @api */
|
||||
interface CompositeTraversableType extends Type
|
||||
interface CompositeTraversableType extends CompositeType
|
||||
{
|
||||
public function keyType(): ArrayKeyType;
|
||||
|
||||
|
14
src/Type/CompositeType.php
Normal file
14
src/Type/CompositeType.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Type;
|
||||
|
||||
/** @api */
|
||||
interface CompositeType extends Type
|
||||
{
|
||||
/**
|
||||
* @return iterable<Type>
|
||||
*/
|
||||
public function traverse(): iterable;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Type;
|
||||
|
||||
/** @api */
|
||||
interface TraversableType extends Type
|
||||
{
|
||||
}
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function is_array;
|
||||
@ -98,6 +99,15 @@ final class ArrayType implements CompositeTraversableType
|
||||
return $this->subType;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
yield $this->subType;
|
||||
|
||||
if ($this->subType instanceof CompositeType) {
|
||||
yield from $this->subType->traverse();
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->signature;
|
||||
|
@ -5,12 +5,13 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function is_a;
|
||||
|
||||
/** @api */
|
||||
final class ClassType implements ObjectType
|
||||
final class ClassType implements ObjectType, CompositeType
|
||||
{
|
||||
/** @var class-string */
|
||||
private string $className;
|
||||
@ -64,6 +65,17 @@ final class ClassType implements ObjectType
|
||||
return is_a($this->className, $other->className(), true);
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
foreach ($this->generics as $type) {
|
||||
yield $type;
|
||||
|
||||
if ($type instanceof CompositeType) {
|
||||
yield from $type->traverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return empty($this->generics)
|
||||
|
@ -6,6 +6,7 @@ namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CombiningType;
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function implode;
|
||||
@ -65,6 +66,17 @@ final class IntersectionType implements CombiningType
|
||||
return true;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
foreach ($this->types as $type) {
|
||||
yield $type;
|
||||
|
||||
if ($type instanceof CompositeType) {
|
||||
yield from $type->traverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectType[]
|
||||
*/
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function is_iterable;
|
||||
@ -84,6 +85,15 @@ final class IterableType implements CompositeTraversableType
|
||||
return $this->subType;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
yield $this->subType;
|
||||
|
||||
if ($this->subType instanceof CompositeType) {
|
||||
yield from $this->subType->traverse();
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->signature;
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function is_array;
|
||||
@ -91,6 +92,15 @@ final class ListType implements CompositeTraversableType
|
||||
return $this->subType;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
yield $this->subType;
|
||||
|
||||
if ($this->subType instanceof CompositeType) {
|
||||
yield from $this->subType->traverse();
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->signature;
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function is_array;
|
||||
@ -94,6 +95,15 @@ final class NonEmptyArrayType implements CompositeTraversableType
|
||||
return $this->subType;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
yield $this->subType;
|
||||
|
||||
if ($this->subType instanceof CompositeType) {
|
||||
yield from $this->subType->traverse();
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->signature;
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function count;
|
||||
@ -99,6 +100,15 @@ final class NonEmptyListType implements CompositeTraversableType
|
||||
return $this->subType;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
yield $this->subType;
|
||||
|
||||
if ($this->subType instanceof CompositeType) {
|
||||
yield from $this->subType->traverse();
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->signature;
|
||||
|
@ -6,7 +6,7 @@ namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayElementDuplicatedKey;
|
||||
use CuyZ\Valinor\Type\TraversableType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
use function array_diff;
|
||||
@ -18,7 +18,7 @@ use function in_array;
|
||||
use function is_array;
|
||||
|
||||
/** @api */
|
||||
final class ShapedArrayType implements TraversableType
|
||||
final class ShapedArrayType implements CompositeType
|
||||
{
|
||||
/** @var ShapedArrayElement[] */
|
||||
private array $elements;
|
||||
@ -120,6 +120,17 @@ final class ShapedArrayType implements TraversableType
|
||||
return true;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
foreach ($this->elements as $element) {
|
||||
yield $type = $element->type();
|
||||
|
||||
if ($type instanceof CompositeType) {
|
||||
yield from $type->traverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShapedArrayElement[]
|
||||
*/
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Type\CombiningType;
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\Exception\ForbiddenMixedType;
|
||||
|
||||
@ -82,6 +83,17 @@ final class UnionType implements CombiningType
|
||||
return false;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
foreach ($this->types as $type) {
|
||||
yield $type;
|
||||
|
||||
if ($type instanceof CompositeType) {
|
||||
yield from $type->traverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function types(): array
|
||||
{
|
||||
return $this->types;
|
||||
|
39
tests/Fake/Type/FakeCompositeType.php
Normal file
39
tests/Fake/Type/FakeCompositeType.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Fake\Type;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
final class FakeCompositeType implements CompositeType
|
||||
{
|
||||
/** @var Type[] */
|
||||
private array $types;
|
||||
|
||||
public function __construct(Type ...$types)
|
||||
{
|
||||
$this->types = $types;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
yield from $this->types;
|
||||
}
|
||||
|
||||
public function accepts($value): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function matches(Type $other): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return 'FakeCompositeType';
|
||||
}
|
||||
}
|
59
tests/Fake/Type/FakeObjectCompositeType.php
Normal file
59
tests/Fake/Type/FakeObjectCompositeType.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Fake\Type;
|
||||
|
||||
use CuyZ\Valinor\Type\CompositeType;
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use stdClass;
|
||||
|
||||
final class FakeObjectCompositeType implements ObjectType, CompositeType
|
||||
{
|
||||
/** @var class-string */
|
||||
private string $className;
|
||||
|
||||
/** @var array<string, Type> */
|
||||
private array $generics;
|
||||
|
||||
/**
|
||||
* @param class-string $className
|
||||
* @param array<string, Type> $generics
|
||||
*/
|
||||
public function __construct(string $className = stdClass::class, array $generics = [])
|
||||
{
|
||||
$this->className = $className;
|
||||
$this->generics = $generics;
|
||||
}
|
||||
|
||||
public function className(): string
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
public function generics(): array
|
||||
{
|
||||
return $this->generics;
|
||||
}
|
||||
|
||||
public function accepts($value): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function matches(Type $other): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function traverse(): iterable
|
||||
{
|
||||
yield from $this->generics;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayType;
|
||||
@ -134,6 +135,7 @@ final class ArrayTypeTest extends TestCase
|
||||
|
||||
self::assertTrue($arrayType->matches($iterableType));
|
||||
}
|
||||
|
||||
public function test_does_not_match_invalid_iterable_type(): void
|
||||
{
|
||||
$typeA = new FakeType();
|
||||
@ -180,4 +182,26 @@ final class ArrayTypeTest extends TestCase
|
||||
|
||||
self::assertFalse(ArrayType::native()->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_type(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
|
||||
$type = new ArrayType(ArrayKeyType::default(), $subType);
|
||||
|
||||
self::assertCount(1, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
$compositeType = new FakeCompositeType($subType);
|
||||
|
||||
$type = new ArrayType(ArrayKeyType::default(), $compositeType);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
self::assertContains($compositeType, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\ClassType;
|
||||
use CuyZ\Valinor\Type\Types\MixedType;
|
||||
@ -106,4 +107,31 @@ final class ClassTypeTest extends TestCase
|
||||
|
||||
self::assertFalse($classType->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_types(): void
|
||||
{
|
||||
$subTypeA = new FakeType();
|
||||
$subTypeB = new FakeType();
|
||||
|
||||
$type = new ClassType(stdClass::class, [
|
||||
'TemplateA' => $subTypeA,
|
||||
'TemplateB' => $subTypeB,
|
||||
]);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subTypeA, $type->traverse());
|
||||
self::assertContains($subTypeB, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
$compositeType = new FakeCompositeType($subType);
|
||||
|
||||
$type = new ClassType(stdClass::class, ['Template' => $compositeType]);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
self::assertContains($compositeType, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeObjectCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeObjectType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\IntersectionType;
|
||||
@ -133,4 +134,32 @@ final class IntersectionTypeTest extends TestCase
|
||||
|
||||
self::assertFalse($intersectionType->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_types(): void
|
||||
{
|
||||
$objectTypeA = new FakeObjectType();
|
||||
$objectTypeB = new FakeObjectType();
|
||||
|
||||
$type = new IntersectionType($objectTypeA, $objectTypeB);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($objectTypeA, $type->traverse());
|
||||
self::assertContains($objectTypeB, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subTypeA = new FakeType();
|
||||
$subTypeB = new FakeType();
|
||||
$objectTypeA = new FakeObjectCompositeType(stdClass::class, ['Template' => $subTypeA]);
|
||||
$objectTypeB = new FakeObjectCompositeType(stdClass::class, ['Template' => $subTypeB]);
|
||||
|
||||
$type = new IntersectionType($objectTypeA, $objectTypeB);
|
||||
|
||||
self::assertCount(4, $type->traverse());
|
||||
self::assertContains($subTypeA, $type->traverse());
|
||||
self::assertContains($subTypeB, $type->traverse());
|
||||
self::assertContains($objectTypeA, $type->traverse());
|
||||
self::assertContains($objectTypeB, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
use CuyZ\Valinor\Type\Types\NativeStringType;
|
||||
@ -146,4 +147,26 @@ final class IterableTypeTest extends TestCase
|
||||
|
||||
self::assertFalse(IterableType::native()->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_type(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
|
||||
$type = new IterableType(ArrayKeyType::default(), $subType);
|
||||
|
||||
self::assertCount(1, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
$compositeType = new FakeCompositeType($subType);
|
||||
|
||||
$type = new IterableType(ArrayKeyType::default(), $compositeType);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
self::assertContains($compositeType, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayType;
|
||||
@ -188,4 +189,26 @@ final class ListTypeTest extends TestCase
|
||||
|
||||
self::assertFalse(ListType::native()->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_type(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
|
||||
$type = new ListType($subType);
|
||||
|
||||
self::assertCount(1, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
$compositeType = new FakeCompositeType($subType);
|
||||
|
||||
$type = new ListType($compositeType);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
self::assertContains($compositeType, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
use CuyZ\Valinor\Type\Types\MixedType;
|
||||
@ -149,4 +150,26 @@ final class NonEmptyArrayTypeTest extends TestCase
|
||||
|
||||
self::assertFalse(NonEmptyArrayType::native()->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_type(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
|
||||
$type = new NonEmptyArrayType(ArrayKeyType::default(), $subType);
|
||||
|
||||
self::assertCount(1, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
$compositeType = new FakeCompositeType($subType);
|
||||
|
||||
$type = new NonEmptyArrayType(ArrayKeyType::default(), $compositeType);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
self::assertContains($compositeType, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayType;
|
||||
@ -235,4 +236,26 @@ final class NonEmptyListTypeTest extends TestCase
|
||||
|
||||
self::assertFalse(NonEmptyListType::native()->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_type(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
|
||||
$type = new NonEmptyListType($subType);
|
||||
|
||||
self::assertCount(1, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subType = new FakeType();
|
||||
$compositeType = new FakeCompositeType($subType);
|
||||
|
||||
$type = new NonEmptyListType($compositeType);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subType, $type->traverse());
|
||||
self::assertContains($compositeType, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayElementDuplicatedKey;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
@ -168,4 +169,38 @@ final class ShapedArrayTypeTest extends TestCase
|
||||
|
||||
self::assertFalse($this->type->matches($unionType));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_types(): void
|
||||
{
|
||||
$subTypeA = new FakeType();
|
||||
$subTypeB = new FakeType();
|
||||
|
||||
$type = new ShapedArrayType(
|
||||
new ShapedArrayElement(new StringValueType('foo'), $subTypeA),
|
||||
new ShapedArrayElement(new StringValueType('bar'), $subTypeB),
|
||||
);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subTypeA, $type->traverse());
|
||||
self::assertContains($subTypeB, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subTypeA = new FakeType();
|
||||
$subTypeB = new FakeType();
|
||||
$compositeTypeA = new FakeCompositeType($subTypeA);
|
||||
$compositeTypeB = new FakeCompositeType($subTypeB);
|
||||
|
||||
$type = new ShapedArrayType(
|
||||
new ShapedArrayElement(new StringValueType('foo'), $compositeTypeA),
|
||||
new ShapedArrayElement(new StringValueType('bar'), $compositeTypeB),
|
||||
);
|
||||
|
||||
self::assertCount(4, $type->traverse());
|
||||
self::assertContains($subTypeA, $type->traverse());
|
||||
self::assertContains($subTypeB, $type->traverse());
|
||||
self::assertContains($compositeTypeA, $type->traverse());
|
||||
self::assertContains($compositeTypeB, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
|
||||
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeCompositeType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Types\Exception\ForbiddenMixedType;
|
||||
use CuyZ\Valinor\Type\Types\MixedType;
|
||||
@ -135,4 +136,32 @@ final class UnionTypeTest extends TestCase
|
||||
|
||||
self::assertFalse($unionTypeA->matches($unionTypeB));
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_sub_types(): void
|
||||
{
|
||||
$subTypeA = new FakeType();
|
||||
$subTypeB = new FakeType();
|
||||
|
||||
$type = new UnionType($subTypeA, $subTypeB);
|
||||
|
||||
self::assertCount(2, $type->traverse());
|
||||
self::assertContains($subTypeA, $type->traverse());
|
||||
self::assertContains($subTypeB, $type->traverse());
|
||||
}
|
||||
|
||||
public function test_traverse_type_yields_types_recursively(): void
|
||||
{
|
||||
$subTypeA = new FakeType();
|
||||
$subTypeB = new FakeType();
|
||||
$compositeTypeA = new FakeCompositeType($subTypeA);
|
||||
$compositeTypeB = new FakeCompositeType($subTypeB);
|
||||
|
||||
$type = new UnionType($compositeTypeA, $compositeTypeB);
|
||||
|
||||
self::assertCount(4, $type->traverse());
|
||||
self::assertContains($subTypeA, $type->traverse());
|
||||
self::assertContains($subTypeB, $type->traverse());
|
||||
self::assertContains($compositeTypeA, $type->traverse());
|
||||
self::assertContains($compositeTypeB, $type->traverse());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user