diff --git a/src/Psl/Type/literal_scalar.php b/src/Psl/Type/literal_scalar.php index fb376450..d50fc80d 100644 --- a/src/Psl/Type/literal_scalar.php +++ b/src/Psl/Type/literal_scalar.php @@ -18,7 +18,7 @@ function literal_scalar($value): TypeInterface { /** @psalm-suppress MissingThrowsDocblock */ - union(union(string(), bool()), num())->assert($value); + union(string(), bool(), num())->assert($value); return new Internal\LiteralScalarType($value); } diff --git a/src/Psl/Type/union.php b/src/Psl/Type/union.php index be44d66a..4bd99697 100644 --- a/src/Psl/Type/union.php +++ b/src/Psl/Type/union.php @@ -7,19 +7,26 @@ use Psl; /** - * @template Tl - * @template Tr + * @template T * - * @param TypeInterface $left_type - * @param TypeInterface $right_type + * @param TypeInterface $first + * @param TypeInterface $second + * @param TypeInterface ...$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 + * @return TypeInterface */ function union( - TypeInterface $left_type, - TypeInterface $right_type + TypeInterface $first, + TypeInterface $second, + TypeInterface ...$rest ): TypeInterface { - return new Internal\UnionType($left_type, $right_type); + $accumulated_type = new Internal\UnionType($first, $second); + + foreach ($rest as $type) { + $accumulated_type = new Internal\UnionType($accumulated_type, $type); + } + + return $accumulated_type; } diff --git a/tests/static-analysis/Type/union.php b/tests/static-analysis/Type/union.php new file mode 100644 index 00000000..a1eca209 --- /dev/null +++ b/tests/static-analysis/Type/union.php @@ -0,0 +1,43 @@ +assert('any')); + + /** @psalm-suppress MissingThrowsDocblock */ + takes_valid_state($new_codec->assert('any')); +} diff --git a/tests/unit/Type/UnionTypeTest.php b/tests/unit/Type/UnionTypeTest.php index 147c7dff..1fe925dc 100644 --- a/tests/unit/Type/UnionTypeTest.php +++ b/tests/unit/Type/UnionTypeTest.php @@ -39,7 +39,7 @@ public function getToStringExamples(): iterable { yield [Type\union(Type\bool(), Type\string()), 'bool|string']; yield [Type\union(Type\bool(), Type\float()), 'bool|float']; - yield [Type\union(Type\bool(), Type\union(Type\float(), Type\int())), 'bool|float|int']; + yield [Type\union(Type\bool(), Type\float(), Type\int()), 'bool|float|int']; yield [Type\union(Type\bool(), Type\num()), 'bool|num']; yield [Type\union(Type\bool(), Type\array_key()), 'bool|array-key']; yield [ @@ -58,9 +58,20 @@ public function getToStringExamples(): iterable Type\object(IndexAccessInterface::class), Type\object(CollectionInterface::class) ), - Type\bool() + Type\bool(), + Type\non_empty_string() + ), + '((Psl\Collection\IndexAccessInterface&Psl\Collection\CollectionInterface)|bool)|non-empty-string' + ]; + yield [ + Type\union( + Type\null(), + Type\vec(Type\positive_int()), + Type\literal_scalar('php'), + Type\literal_scalar('still'), + Type\literal_scalar('alive'), ), - '(Psl\Collection\IndexAccessInterface&Psl\Collection\CollectionInterface)|bool' + 'null|list|"php"|"still"|"alive"' ]; } }