Skip to content

Commit

Permalink
[Type] add support for variadic intersection types (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
nzour committed Apr 28, 2021
1 parent e187467 commit 336653f
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 20 deletions.
2 changes: 1 addition & 1 deletion docs/component/type.md
Expand Up @@ -17,7 +17,7 @@
- [dict](./../../src/Psl/Type/dict.php#L20)
- [float](./../../src/Psl/Type/float.php#L10)
- [int](./../../src/Psl/Type/int.php#L10)
- [intersection](./../../src/Psl/Type/intersection.php#L20)
- [intersection](./../../src/Psl/Type/intersection.php#L22)
- [is_array](./../../src/Psl/Type/is_array.php#L20) ( deprecated )
- [is_arraykey](./../../src/Psl/Type/is_arraykey.php#L18) ( deprecated )
- [is_bool](./../../src/Psl/Type/is_bool.php#L20) ( deprecated )
Expand Down
29 changes: 19 additions & 10 deletions src/Psl/Type/intersection.php
Expand Up @@ -7,20 +7,29 @@
use Psl;

/**
* @template Tl
* @template Tr
* @template TFirst
* @template TSecond
* @template TRest
*
* @param TypeInterface<Tl> $left_type
* @param TypeInterface<Tr> $right_type
* @param TypeInterface<TFirst> $first
* @param TypeInterface<TSecond> $second
* @param TypeInterface<TRest> ...$rest
*
* @throws Psl\Exception\InvariantViolationException If $left_type, or $right_type is optional.
* @throws Psl\Exception\InvariantViolationException If $first, $second or one of $rest is optional.
*
* @return TypeInterface<Tl&Tr>
* @return TypeInterface<TFirst&TSecond&TRest>
*/
function intersection(
TypeInterface $left_type,
TypeInterface $right_type
TypeInterface $first,
TypeInterface $second,
TypeInterface ...$rest
): TypeInterface {
/** @var TypeInterface<Tl&Tr> */
return new Internal\IntersectionType($left_type, $right_type);
$accumulated_type = new Internal\IntersectionType($first, $second);

foreach ($rest as $type) {
$accumulated_type = new Internal\IntersectionType($accumulated_type, $type);
}

/** @var TypeInterface<TFirst&TSecond&TRest> */
return $accumulated_type;
}
46 changes: 46 additions & 0 deletions tests/static-analysis/Type/intersection.php
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

use Psl\Collection\Map;
use Psl\Collection\Vector;
use Psl\Result\ResultInterface;
use Psl\Type;

/**
* @psalm-suppress UnusedParam
*
* @param Map&ResultInterface&stdClass&Vector $value
*/
function takes_valid_intersection($value): void
{
}

function test(): void
{
/** @psalm-suppress MissingThrowsDocblock */
$old_school_codec = Type\intersection(
Type\object(Map::class),
Type\intersection(
Type\object(ResultInterface::class),
Type\intersection(
Type\object(stdClass::class),
Type\object(Vector::class),
)
),
);

/** @psalm-suppress MissingThrowsDocblock */
$new_codec = Type\intersection(
Type\object(Map::class),
Type\object(ResultInterface::class),
Type\object(stdClass::class),
Type\object(Vector::class),
);

/** @psalm-suppress MissingThrowsDocblock */
takes_valid_intersection($old_school_codec->assert('any'));

/** @psalm-suppress MissingThrowsDocblock */
takes_valid_intersection($new_codec->assert('any'));
}
40 changes: 31 additions & 9 deletions tests/unit/Type/IntersectionTypeTest.php
Expand Up @@ -13,7 +13,7 @@ final class IntersectionTypeTest extends TypeTest
{
public function testIntersectionLeft(): void
{
$intersection = Type\intersection(Type\array_key(), Type\int());
$intersection = Type\intersection(Type\array_key(), Type\int(), Type\positive_int());

static::assertSame(1, $intersection->coerce('1'));
}
Expand Down Expand Up @@ -53,14 +53,36 @@ public function getToStringExamples(): iterable
'Psl\Collection\IndexAccessInterface&Psl\Collection\CollectionInterface'
];

yield [Type\intersection(
Type\object(IndexAccessInterface::class),
Type\union(Type\object(CollectionInterface::class), Type\object(Iterator::class))
), 'Psl\Collection\IndexAccessInterface&(Psl\Collection\CollectionInterface|Iterator)'];
yield [
Type\intersection(
Type\object(IndexAccessInterface::class),
Type\union(
Type\object(CollectionInterface::class),
Type\object(Iterator::class)
)
),
'Psl\Collection\IndexAccessInterface&(Psl\Collection\CollectionInterface|Iterator)'
];

yield [Type\intersection(
Type\union(Type\object(CollectionInterface::class), Type\object(Iterator::class)),
Type\object(IndexAccessInterface::class)
), '(Psl\Collection\CollectionInterface|Iterator)&Psl\Collection\IndexAccessInterface'];
yield [
Type\intersection(
Type\union(
Type\object(CollectionInterface::class),
Type\object(Iterator::class)
),
Type\object(IndexAccessInterface::class)
),
'(Psl\Collection\CollectionInterface|Iterator)&Psl\Collection\IndexAccessInterface'
];

yield [
Type\intersection(
Type\object(IndexAccessInterface::class),
Type\object(CollectionInterface::class),
Type\object(Iterator::class),
Type\shape(['id' => Type\string()]),
),
'Psl\Collection\IndexAccessInterface&Psl\Collection\CollectionInterface&Iterator&array{\'id\': string}'
];
}
}

0 comments on commit 336653f

Please sign in to comment.