diff --git a/src/Compiler/MapperFactory/DefaultMapperCompilerFactory.php b/src/Compiler/MapperFactory/DefaultMapperCompilerFactory.php index 5452847..ecc3fd2 100644 --- a/src/Compiler/MapperFactory/DefaultMapperCompilerFactory.php +++ b/src/Compiler/MapperFactory/DefaultMapperCompilerFactory.php @@ -19,6 +19,7 @@ use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; +use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; use PHPStan\PhpDocParser\Lexer\Lexer; use PHPStan\PhpDocParser\Parser\PhpDocParser; use PHPStan\PhpDocParser\Parser\TokenIterator; @@ -57,6 +58,7 @@ use function count; use function enum_exists; use function interface_exists; +use function strcasecmp; use function strtolower; use function substr; @@ -175,6 +177,23 @@ public function create(TypeNode $type, array $options = []): MapperCompiler return new MapArrayShape($items, $type->sealed); } + if ($type instanceof UnionTypeNode) { + $isNullable = false; + $subTypesWithoutNull = []; + + foreach ($type->types as $subType) { + if ($subType instanceof IdentifierTypeNode && strcasecmp($subType->name, 'null') === 0) { + $isNullable = true; + } else { + $subTypesWithoutNull[] = $subType; + } + } + + if ($isNullable && count($subTypesWithoutNull) === 1) { + return new MapNullable($this->createInner($subTypesWithoutNull[0], $options)); + } + } + throw CannotCreateMapperCompilerException::fromType($type); } diff --git a/tests/Compiler/MapperFactory/DefaultMapperCompilerFactoryTest.php b/tests/Compiler/MapperFactory/DefaultMapperCompilerFactoryTest.php index 0042215..8a0a34d 100644 --- a/tests/Compiler/MapperFactory/DefaultMapperCompilerFactoryTest.php +++ b/tests/Compiler/MapperFactory/DefaultMapperCompilerFactoryTest.php @@ -174,6 +174,18 @@ public static function provideCreateOkData(): iterable new MapString(), ]; + yield '?int' => [ + '?int', + [], + new MapNullable(new MapInt()), + ]; + + yield 'int|null' => [ + 'int|null', + [], + new MapNullable(new MapInt()), + ]; + yield 'int[]' => [ 'int[]', [],