diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index cce99c5e0f0..a0045f3e898 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -711,17 +711,16 @@ private static function getTypeFromGenericTree( ); } - $param_union_types = array_values($generic_params[0]->getAtomicTypes()); - - if (count($param_union_types) > 1) { - throw new TypeParseTreeException('Union types are not allowed in class string param'); - } + $types = []; + foreach ($generic_params[0]->getAtomicTypes() as $type) { + if (!$type instanceof TNamedObject) { + throw new TypeParseTreeException('Class string param should be a named object'); + } - if (!$param_union_types[0] instanceof TNamedObject) { - throw new TypeParseTreeException('Class string param should be a named object'); + $types []= new TClassString($type->value, $type, false, false, false, $from_docblock); } - return new TClassString($class_name, $param_union_types[0], false, false, false, $from_docblock); + return new Union($types); } if ($generic_type_value === 'class-string-map') { diff --git a/tests/ClassLikeStringTest.php b/tests/ClassLikeStringTest.php index 5a2213a0408..8ac7e60b4a8 100644 --- a/tests/ClassLikeStringTest.php +++ b/tests/ClassLikeStringTest.php @@ -859,6 +859,26 @@ function foo(A $a): void { if (get_class($a) === A::class) {} }', ], + 'classStringUnion' => [ + 'code' => '|class-string */ + public ?string $bar = null; + /** @var class-string */ + public ?string $baz = null; + } + + class TypeOne {} + + class TypeTwo {} + + $foo = new Foo; + $foo->bar = TypeOne::class; + $foo->bar = TypeOne::class; + $foo->baz = TypeTwo::class; + $foo->baz = TypeTwo::class;' + ] ]; }