Skip to content

Commit fe2479b

Browse files
committed
Improve API
1 parent c4a5660 commit fe2479b

16 files changed

Lines changed: 237 additions & 190 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
],
7272
"infection": "tools/infection/vendor/bin/infection --show-mutations",
7373
"normalize": "@composer bin composer-normalize normalize --diff ../../composer.json",
74-
"phpstan": "tools/phpstan/vendor/bin/phpstan analyze",
74+
"phpstan": "tools/phpstan/vendor/bin/phpstan analyze --memory-limit=-1",
7575
"pre-command-run": "mkdir -p var",
7676
"psalm": "tools/psalm/vendor/bin/psalm --show-info --no-diff --no-cache",
7777
"rector": "tools/rector/vendor/bin/rector process",

generator/Visitor/Stringify.php

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public function listT(ListT $type): string
100100
{
101101
$value = $type->value->accept($this);
102102

103-
$elements = $this->arrayElements($type->elements);
103+
$elements = implode(', ', array_map(fn(Type $type): string => $type->accept($this), $type->elements));
104104

105105
if ($value === 'never') {
106106
return \sprintf('list{%s}', $elements);
@@ -122,7 +122,7 @@ public function arrayT(ArrayT $type): string
122122
{
123123
$value = $type->value->accept($this);
124124

125-
$elements = $this->arrayElements($type->elements);
125+
$elements = implode(', ', array_map($this->arrayElement(...), $type->elements));
126126

127127
if ($value === 'never') {
128128
return \sprintf('array{%s}', $elements);
@@ -145,27 +145,16 @@ public function arrayT(ArrayT $type): string
145145
}
146146

147147
/**
148-
* @param array<ArrayElement> $elements
148+
* @return non-empty-string
149149
*/
150-
private function arrayElements(array $elements): string
150+
protected function arrayElement(ArrayElement $element): string
151151
{
152-
if (array_is_list($elements) && array_all($elements, static fn(ArrayElement $e) => !$e->isOptional)) {
153-
return implode(', ', array_map(
154-
fn(ArrayElement $element): string => $element->type->accept($this),
155-
$elements,
156-
));
157-
}
158-
159-
return implode(', ', array_map(
160-
fn(int|string $key, ArrayElement $element): string => \sprintf(
161-
'%s%s: %s',
162-
\is_int($key) ? $key : $this->stringValueT(new StringValueT($key)),
163-
$element->isOptional ? '?' : '',
164-
$element->type->accept($this),
165-
),
166-
array_keys($elements),
167-
$elements,
168-
));
152+
return \sprintf(
153+
'%s%s: %s',
154+
\is_int($element->key) ? $element->key : $this->stringValueT(new StringValueT($element->key)),
155+
$element->isOptional ? '?' : '',
156+
$element->type->accept($this),
157+
);
169158
}
170159

171160
#[\Override]
@@ -208,7 +197,7 @@ public function objectT(ObjectT $type): string
208197
/**
209198
* @return non-empty-string
210199
*/
211-
private function property(Property $property): string
200+
protected function property(Property $property): string
212201
{
213202
return \sprintf('%s%s: %s', $property->name, $property->isOptional ? '?' : '', $property->type->accept($this));
214203
}
@@ -256,7 +245,7 @@ public function callableT(CallableT $type): string
256245
'callable%s(%s): %s',
257246
$this->templates($type->templates),
258247
implode(', ', array_map($this->parameter(...), $type->parameters)),
259-
$type->returns->accept($this),
248+
$type->return->accept($this),
260249
);
261250
}
262251

@@ -267,31 +256,52 @@ public function closureT(ClosureT $type): string
267256
'Closure%s(%s): %s',
268257
$this->templates($type->templates),
269258
implode(', ', array_map($this->parameter(...), $type->parameters)),
270-
$type->returns->accept($this),
259+
$type->return->accept($this),
271260
);
272261
}
273262

274263
/**
275264
* @return non-empty-string
276265
*/
277-
private function parameter(Parameter $parameter): string
266+
protected function parameter(Parameter $parameter): string
278267
{
279-
/** @phpstan-ignore return.type */
280-
return \sprintf(
281-
'%s%s%s%s',
282-
$parameter->type->accept($this),
283-
$parameter->isPassedByReference ? '&' : '',
284-
$parameter->isVariadic ? '...' : '',
285-
$parameter->hasDefault ? '=' : '',
286-
);
268+
$string = $parameter->type->accept($this);
269+
270+
if ($parameter->name !== null) {
271+
$string .= ' ';
272+
}
273+
274+
if ($parameter->isPassedByReference) {
275+
$string .= '&';
276+
277+
// todo $parameter->outType
278+
}
279+
280+
if ($parameter->isVariadic) {
281+
$string .= '...';
282+
}
283+
284+
if ($parameter->name !== null) {
285+
$string .= '$' . $parameter->name;
286+
}
287+
288+
if ($parameter->hasDefault) {
289+
$string .= '=';
290+
291+
if ($parameter->defaultType !== null) {
292+
$string .= $parameter->defaultType->accept($this);
293+
}
294+
}
295+
296+
return $string;
287297
}
288298

289299
/**
290300
* @param non-empty-string $name
291301
* @param list<Type> $templateArguments
292302
* @return non-empty-string
293303
*/
294-
private function constructor(string $name, array $templateArguments): string
304+
protected function constructor(string $name, array $templateArguments): string
295305
{
296306
if ($templateArguments === []) {
297307
return $name;
@@ -384,7 +394,7 @@ public function ternaryT(TernaryT $type): string
384394
/**
385395
* @param list<Template> $templates
386396
*/
387-
private function templates(array $templates): string
397+
protected function templates(array $templates): string
388398
{
389399
if ($templates === []) {
390400
return '';
@@ -393,13 +403,13 @@ private function templates(array $templates): string
393403
return \sprintf('<%s>', implode(', ', array_map($this->template(...), $templates)));
394404
}
395405

396-
private function template(Template $template): string
406+
protected function template(Template $template): string
397407
{
398408
$lowerBound = $template->lowerBound->accept($this);
399409
$upperBound = $template->upperBound->accept($this);
400410

401411
return \sprintf(
402-
'%s%s%s%s',
412+
'%s%s%s%s%s',
403413
match ($template->variance) {
404414
Variance::Invariant => '',
405415
Variance::Covariant => 'out ',
@@ -408,6 +418,7 @@ private function template(Template $template): string
408418
$this->templateNames()[$template->type] ??= $template->name,
409419
$upperBound === 'mixed' ? '' : ' of ' . $upperBound,
410420
$lowerBound === 'never' ? '' : ' super ' . $lowerBound,
421+
$template->default === null ? '' : ' = ' . $template->default->accept($this),
411422
);
412423
}
413424

@@ -417,7 +428,7 @@ private function template(Template $template): string
417428
/**
418429
* @return \SplObjectStorage<TemplateT, non-empty-string>
419430
*/
420-
private function templateNames(): \SplObjectStorage
431+
final protected function templateNames(): \SplObjectStorage
421432
{
422433
if ($this->templateNames !== null) {
423434
return $this->templateNames;

generator/spec.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@
4343
single('numeric', 'numeric', 'union([int, float, numericString])'),
4444
single('scalar', 'scalar', 'union([bool, int, float, string])'),
4545
// array
46-
constr('list', 'list<V>', [tpl('V')], [prop('value', 'Type<V>', MixedT::T), prop('elements', 'list<ArrayElement>'), prop('isNonEmpty', 'bool')]),
46+
constr('list', 'T', [tpl('T', 'list')], [prop('value', 'Type', MixedT::T), prop('elements', 'list<Type>'), prop('isNonEmpty', 'bool')]),
4747
single('arrayDefault', 'array', 'array()'),
48-
constr('array', 'array<K, V>', [tpl('K', 'array-key'), tpl('V')], [prop('key', 'Type<K>', ArrayKeyT::T), prop('value', 'Type<V>', MixedT::T), prop('elements', 'array<ArrayElement>'), prop('isNonEmpty', 'bool')]),
48+
constr('array', 'T', [tpl('T', 'array')], [prop('key', 'Type', ArrayKeyT::T), prop('value', 'Type', MixedT::T), prop('elements', 'list<ArrayElement>'), prop('isNonEmpty', 'bool')]),
4949
// object
5050
single('objectDefault', 'object', 'object()'),
5151
constr('namedObject', 'T', [tpl('T', 'object')], [prop('class', 'class-string<T>'), prop('templateArguments', 'list<Type>')], 'object(superTypes: [$t])'),
@@ -61,9 +61,9 @@
6161
constr('iterable', 'iterable<K, V>', [tpl('K'), tpl('V')], [prop('key', 'Type<K>', MixedT::T), prop('value', 'Type<V>', MixedT::T)]),
6262
// callable
6363
single('callableDefault', 'callable', 'callable()'),
64-
constr('callable', 'T', [tpl('T', 'callable')], [prop('templates', 'list<Template<Variance::Invariant>>'), prop('parameters', 'list<Parameter>'), prop('returns', 'Type', MixedT::T)]),
64+
constr('callable', 'T', [tpl('T', 'callable')], [prop('templates', 'list<Template<Variance::Invariant>>'), prop('parameters', 'list<Parameter>'), prop('return', 'Type', MixedT::T)]),
6565
single('closureDefault', 'Closure', 'namedObject(Closure::class)'),
66-
constr('closure', 'T', [tpl('T', 'Closure')], [prop('templates', 'list<Template<Variance::Invariant>>'), prop('parameters', 'list<Parameter>'), prop('returns', 'Type', MixedT::T)], "intersection([\nclosureDefault,\ncallable(\$templates, \$parameters, \$returns),\n])"),
66+
constr('closure', 'T', [tpl('T', 'Closure')], [prop('templates', 'list<Template<Variance::Invariant>>'), prop('parameters', 'list<Parameter>'), prop('return', 'Type', MixedT::T)], "intersection([\nclosureDefault,\ncallable(\$templates, \$parameters, \$return),\n])"),
6767
// resource
6868
single('resource', 'resource'),
6969
// intersection

src/ArrayElement.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
final readonly class ArrayElement
1111
{
1212
public function __construct(
13+
public int|string $key,
1314
public Type $type,
1415
public bool $isOptional = false,
1516
) {}

src/ArrayT.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,14 @@
1010

1111
/**
1212
* @api
13-
* @template-covariant K of array-key = array-key
14-
* @template-covariant V = mixed
15-
* @implements Type<array<K, V>>
13+
* @template-covariant T of array = array
14+
* @implements Type<T>
1615
* @codeCoverageIgnore
1716
*/
1817
final readonly class ArrayT implements Type
1918
{
2019
/**
21-
* @param Type<K> $key
22-
* @param Type<V> $value
23-
* @param array<ArrayElement> $elements
20+
* @param list<ArrayElement> $elements
2421
*/
2522
public function __construct(
2623
public Type $key = ArrayKeyT::T,

src/CallableT.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
public function __construct(
2424
public array $templates = [],
2525
public array $parameters = [],
26-
public Type $returns = MixedT::T,
26+
public Type $return = MixedT::T,
2727
) {}
2828

2929
#[\Override]

src/ClosureT.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
public function __construct(
2626
public array $templates = [],
2727
public array $parameters = [],
28-
public Type $returns = MixedT::T,
28+
public Type $return = MixedT::T,
2929
) {}
3030

3131
#[\Override]

src/Internal/Is.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,25 +230,25 @@ public function arrayT(ArrayT $type): mixed
230230
return false;
231231
}
232232

233-
foreach ($type->elements as $key => $element) {
234-
if (!\array_key_exists($key, $this->value)) {
233+
$remainingElements = $this->value;
234+
235+
foreach ($type->elements as $element) {
236+
if (!\array_key_exists($element->key, $this->value)) {
235237
if ($element->isOptional) {
236238
continue;
237239
}
238240

239241
return false;
240242
}
241243

242-
if (!is($this->value[$key], $element->type)) {
244+
if (!is($this->value[$element->key], $element->type)) {
243245
return false;
244246
}
245-
}
246247

247-
foreach ($this->value as $key => $value) {
248-
if (isset($type->elements[$key])) {
249-
continue;
250-
}
248+
unset($remainingElements[$element->key]);
249+
}
251250

251+
foreach ($remainingElements as $key => $value) {
252252
/** @phpstan-ignore function.alreadyNarrowedType, function.alreadyNarrowedType */
253253
if (!is($key, $type->key) || !is($value, $type->value)) {
254254
return false;

src/ListT.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010

1111
/**
1212
* @api
13-
* @template-covariant V = mixed
14-
* @implements Type<list<V>>
13+
* @template-covariant T of list = list
14+
* @implements Type<T>
1515
* @codeCoverageIgnore
1616
*/
1717
final readonly class ListT implements Type
1818
{
1919
/**
20-
* @param Type<V> $value
21-
* @param list<ArrayElement> $elements
20+
* @param list<Type> $elements
2221
*/
2322
public function __construct(
2423
public Type $value = MixedT::T,

src/Optional.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Typhoon\Type;
6+
7+
/**
8+
* @internal
9+
*/
10+
final readonly class Optional
11+
{
12+
public function __construct(
13+
public Type $type,
14+
) {}
15+
}

0 commit comments

Comments
 (0)