Skip to content

Commit 519691b

Browse files
GromNaNdg
authored andcommitted
Support union type in Type::nullable (#141)
1 parent 535bb03 commit 519691b

File tree

3 files changed

+45
-8
lines changed

3 files changed

+45
-8
lines changed

readme.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,9 @@ Each type or union/intersection type can be passed as a string, you can also use
538538
use Nette\PhpGenerator\Type;
539539

540540
$member->setType('array'); // or Type::Array;
541-
$member->setType('array|string'); // or Type::union('array', 'string')
541+
$member->setType('?array'); // or Type::nullable(Type::Array);
542+
$member->setType('array|string'); // or Type::union(Type::Array, Type::String)
543+
$member->setType('array|string|null'); // or Type::nullable(Type::union(Type::Array, Type::String))
542544
$member->setType('Foo&Bar'); // or Type::intersection(Foo::class, Bar::class)
543545
$member->setType(null); // removes type
544546
```

src/PhpGenerator/Type.php

+13-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace Nette\PhpGenerator;
1111

12+
use Nette;
13+
1214

1315
/**
1416
* PHP return, property and parameter types.
@@ -85,7 +87,17 @@ class Type
8587

8688
public static function nullable(string $type, bool $nullable = true): string
8789
{
88-
return ($nullable ? '?' : '') . ltrim($type, '?');
90+
if (str_contains($type, '&')) {
91+
return $nullable
92+
? throw new Nette\InvalidArgumentException('Intersection types cannot be nullable.')
93+
: $type;
94+
}
95+
96+
$nnType = preg_replace('#^\?|^null\||\|null(?=\||$)#i', '', $type);
97+
if ($nullable && $type === $nnType) {
98+
$type = str_contains($type, '|') ? $type . '|null' : '?' . $type;
99+
}
100+
return $nullable ? $type : $nnType;
89101
}
90102

91103

tests/PhpGenerator/Type.phpt

+29-6
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,35 @@ use Nette\PhpGenerator\Type;
66
use Tester\Assert;
77
require __DIR__ . '/../bootstrap.php';
88

9+
// Nullable
10+
Assert::same('?int', Type::nullable(Type::Int));
11+
Assert::same('int', Type::nullable(Type::Int, nullable: false));
912

10-
Assert::same('A|string', Type::union(A::class, Type::String));
13+
Assert::same('?int', Type::nullable('?int'));
14+
Assert::same('int', Type::nullable('?int', nullable: false));
15+
16+
Assert::same('int|float|string|null', Type::nullable('int|float|string'));
17+
Assert::same('int|float|string', Type::nullable('int|float|string', nullable: false));
18+
19+
Assert::same('NULL|int|float|string', Type::nullable('NULL|int|float|string'));
20+
Assert::same('int|float|string', Type::nullable('NULL|int|float|string', nullable: false));
21+
22+
Assert::same('int|float|string|null', Type::nullable('int|float|string|null'));
23+
Assert::same('int|float|string', Type::nullable('int|float|string|null', nullable: false));
1124

12-
Assert::same('?A', Type::nullable(A::class));
13-
Assert::same('?A', Type::nullable(A::class));
14-
Assert::same('A', Type::nullable(A::class, nullable: false));
25+
Assert::same('int|float|null|string', Type::nullable('int|float|null|string'));
26+
Assert::same('int|float|string', Type::nullable('int|float|null|string', nullable: false));
27+
28+
Assert::exception(
29+
fn() => Type::nullable('Foo&Bar'),
30+
Nette\InvalidArgumentException::class,
31+
'Intersection types cannot be nullable.',
32+
);
33+
Assert::same('Foo&Bar', Type::nullable('Foo&Bar', nullable: false));
34+
35+
36+
// Union
37+
Assert::same('A|string', Type::union(A::class, Type::String));
1538

16-
Assert::same('?A', Type::nullable('?A'));
17-
Assert::same('A', Type::nullable('?A', nullable: false));
39+
// Intersection
40+
Assert::same('A&string', Type::intersection(A::class, Type::String));

0 commit comments

Comments
 (0)