Skip to content

Commit

Permalink
Support more array intersections in type parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Sep 14, 2020
1 parent 6afd3d1 commit d1ac38b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 5 deletions.
40 changes: 35 additions & 5 deletions src/Psalm/Internal/Type/TypeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
use function strpos;
use function strtolower;
use function substr;
use function reset;
use function end;
use function array_pop;

class TypeParser
{
Expand Down Expand Up @@ -411,17 +414,33 @@ function (ParseTree $child_tree) use ($codebase, $template_type_map, $type_alias
$parse_tree->children
);

$onlyObjectLike = true;
$onlyTKeyedArray = true;

$first_type = \reset($intersection_types);
$last_type = \end($intersection_types);

foreach ($intersection_types as $intersection_type) {
if (!$intersection_type instanceof ObjectLike) {
$onlyObjectLike = false;
if (!$intersection_type instanceof ObjectLike
&& ($intersection_type !== $first_type
|| !$first_type instanceof TArray)
&& ($intersection_type !== $last_type
|| !$last_type instanceof TArray)
) {
$onlyTKeyedArray = false;
break;
}
}

if ($onlyObjectLike) {
if ($onlyTKeyedArray) {
/** @var non-empty-array<string|int, Union> */
$properties = [];

if ($first_type instanceof TArray) {
\array_shift($intersection_types);
} elseif ($last_type instanceof TArray) {
\array_pop($intersection_types);
}

/** @var ObjectLike $intersection_type */
foreach ($intersection_types as $intersection_type) {
foreach ($intersection_type->properties as $property => $property_type) {
Expand All @@ -445,7 +464,18 @@ function (ParseTree $child_tree) use ($codebase, $template_type_map, $type_alias
$properties[$property] = $intersection_type;
}
}
return new ObjectLike($properties);

$keyed_array = new ObjectLike($properties);

if ($first_type instanceof TArray) {
$keyed_array->previous_key_type = $first_type->type_params[0];
$keyed_array->previous_value_type = $first_type->type_params[1];
} elseif ($last_type instanceof TArray) {
$keyed_array->previous_key_type = $last_type->type_params[0];
$keyed_array->previous_value_type = $last_type->type_params[1];
}

return $keyed_array;
}

$keyed_intersection_types = [];
Expand Down
10 changes: 10 additions & 0 deletions tests/TypeParseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ public function testIntersectionOfTKeyedArray(): void
$this->assertSame('array{a: int, b: int}', (string) Type::parseString('array{a: int}&array{b: int}'));
}

public function testIntersectionOfTwoDifferentArrays(): void
{
$this->assertSame('array{a: int}<string, string>', Type::parseString('array{a: int}&array<string, string>')->getId());
}

public function testIntersectionOfTwoDifferentArraysReversed(): void
{
$this->assertSame('array{a: int}<string, string>', Type::parseString('array<string, string>&array{a: int}')->getId());
}

public function testIntersectionOfTKeyedArrayWithMergedProperties(): void
{
$this->assertSame('array{a: int}', (string) Type::parseString('array{a: int}&array{a: mixed}'));
Expand Down

0 comments on commit d1ac38b

Please sign in to comment.