Skip to content

Commit cf0fb81

Browse files
committed
added Type::with()
1 parent 117156e commit cf0fb81

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

src/Utils/Type.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,23 @@ public function __toString(): string
155155
}
156156

157157

158+
/**
159+
* Returns a new type that accepts both the current type and the given type.
160+
*/
161+
public function with(string|self $type): self
162+
{
163+
$type = is_string($type) ? self::fromString($type) : $type;
164+
return match (true) {
165+
$this->allows($type) => $this,
166+
$type->allows($this) => $type,
167+
default => new self(array_unique(
168+
array_merge($this->isIntersection() ? [$this] : $this->types, $type->isIntersection() ? [$type] : $type->types),
169+
SORT_REGULAR,
170+
), '|'),
171+
};
172+
}
173+
174+
158175
/**
159176
* Returns the array of subtypes that make up the compound type as strings.
160177
* @return array<int, string|string[]>

tests/Utils/Type.with.phpt

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Utils\Type::with()
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Utils\Type;
10+
use Tester\Assert;
11+
12+
require __DIR__ . '/../bootstrap.php';
13+
14+
15+
class Foo
16+
{
17+
}
18+
19+
class Bar
20+
{
21+
}
22+
23+
class FooChild extends Foo
24+
{
25+
}
26+
27+
28+
function testTypeHint(string $expected, Type $type): void
29+
{
30+
Assert::same($expected, (string) $type);
31+
eval("function($expected \$_) {};");
32+
}
33+
34+
35+
testTypeHint('string|int', Type::fromString('string')->with('int'));
36+
testTypeHint('string|int|null', Type::fromString('string')->with('?int'));
37+
testTypeHint('string|int|null', Type::fromString('?string')->with('int'));
38+
testTypeHint('string|int|null', Type::fromString('?string')->with('?int'));
39+
testTypeHint('string|int|bool', Type::fromString('string|int')->with('bool'));
40+
testTypeHint('string|int|bool', Type::fromString('string|int')->with('bool|int'));
41+
testTypeHint('(Foo&Bar)|string', Type::fromString('Foo&Bar')->with('string'));
42+
testTypeHint('Foo', Type::fromString('Foo&Bar')->with('Foo'));
43+
testTypeHint('string|(Foo&Bar)', Type::fromString('string')->with('Foo&Bar'));
44+
testTypeHint('(Foo&Bar)|(Foo&FooChild)', Type::fromString('Foo&Bar')->with('Foo&FooChild'));
45+
testTypeHint('(Foo&Bar)|string|int', Type::fromString('(Foo&Bar)|string')->with('int'));
46+
47+
// mixed
48+
testTypeHint('mixed', Type::fromString('string')->with('mixed'));
49+
testTypeHint('mixed', Type::fromString('mixed')->with('null'));
50+
51+
// Already allows - returns same instance
52+
$type = Type::fromString('string');
53+
Assert::same($type, $type->with('string'));
54+
55+
$type = Type::fromString('string|int');
56+
Assert::same($type, $type->with('string'));
57+
58+
$type = Type::fromString('?string');
59+
Assert::same($type, $type->with('string'));
60+
Assert::same($type, $type->with('null'));
61+
62+
$type = Type::fromString('mixed');
63+
Assert::same($type, $type->with('string'));
64+
Assert::same($type, $type->with('Foo'));
65+
66+
$type = Type::fromString('Foo');
67+
Assert::same($type, $type->with('FooChild'));
68+
69+
$type = Type::fromString('Foo|Bar');
70+
Assert::same($type, $type->with('FooChild'));
71+
72+
$type = Type::fromString('string|int|bool');
73+
Assert::same($type, $type->with('int'));
74+
75+
76+
// with Type object
77+
testTypeHint('string|int', Type::fromString('string')->with(Type::fromString('int')));

0 commit comments

Comments
 (0)