diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php index 380b80571..b76bccc81 100644 --- a/src/Psalm/Type.php +++ b/src/Psalm/Type.php @@ -387,18 +387,23 @@ abstract class Type } if ($parse_tree instanceof ParseTree\NullableTree) { - $atomic_type = self::getTypeFromTree($parse_tree->children[0], false, $template_type_names); + $non_nullable_type = self::getTypeFromTree($parse_tree->children[0], false, $template_type_names); - if (!$atomic_type instanceof Atomic) { - throw new \UnexpectedValueException( - 'Was expecting an atomic type, got ' . get_class($atomic_type) - ); + if ($non_nullable_type instanceof Union) { + $non_nullable_type->addType(new TNull); + return $non_nullable_type; } - return TypeCombination::combineTypes([ - new TNull, - $atomic_type - ]); + if ($non_nullable_type instanceof Atomic) { + return TypeCombination::combineTypes([ + new TNull, + $non_nullable_type + ]); + } + + throw new \UnexpectedValueException( + 'Was expecting an atomic or union type, got ' . get_class($non_nullable_type) + ); } if (!$parse_tree instanceof ParseTree\Value) { diff --git a/tests/TypeParseTest.php b/tests/TypeParseTest.php index c4dcf347a..052f15960 100644 --- a/tests/TypeParseTest.php +++ b/tests/TypeParseTest.php @@ -61,6 +61,14 @@ class TypeParseTest extends TestCase $this->assertSame('null|string', (string) Type::parseString('?string')); } + /** + * @return void + */ + public function testNullableUnion() + { + $this->assertSame('string|int|null', (string) Type::parseString('?(string|int)')); + } + /** * @return void */