Skip to content

Commit

Permalink
Merge pull request #88 from innocenzi/feat/null-to-optional
Browse files Browse the repository at this point in the history
feat: support `nullToOptional` config
  • Loading branch information
rubenvanassche authored Oct 4, 2024
2 parents bba3a3d + eb36c2c commit 2a81539
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 17 deletions.
8 changes: 8 additions & 0 deletions src/Actions/TranspileTypeToTypeScriptAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ class TranspileTypeToTypeScriptAction

private ?string $currentClass;

private bool $nullablesAreOptional;

public function __construct(
MissingSymbolsCollection $missingSymbolsCollection,
bool $nullablesAreOptional = false,
?string $currentClass = null
) {
$this->missingSymbolsCollection = $missingSymbolsCollection;
$this->nullablesAreOptional = $nullablesAreOptional;
$this->currentClass = $currentClass;
}

Expand Down Expand Up @@ -84,6 +88,10 @@ private function resolveListType(AbstractList $list): string

private function resolveNullableType(Nullable $nullable): string
{
if ($this->nullablesAreOptional) {
return $this->execute($nullable->getActualType());
}

return "{$this->execute($nullable->getActualType())} | null";
}

Expand Down
2 changes: 2 additions & 0 deletions src/Collectors/DefaultCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ protected function resolveAlreadyTransformedType(ClassTypeReflector $reflector):
{
$missingSymbols = new MissingSymbolsCollection();
$name = $reflector->getName();
$nullablesAreOptional = $this->config->shouldConsiderNullAsOptional();

$transpiler = new TranspileTypeToTypeScriptAction(
$missingSymbols,
$nullablesAreOptional,
$name
);

Expand Down
10 changes: 7 additions & 3 deletions src/Transformers/DtoTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,26 @@ protected function transformProperties(
ReflectionClass $class,
MissingSymbolsCollection $missingSymbols
): string {
$isOptional = ! empty($class->getAttributes(Optional::class));
$isClassOptional = ! empty($class->getAttributes(Optional::class));
$nullablesAreOptional = $this->config->shouldConsiderNullAsOptional();

return array_reduce(
$this->resolveProperties($class),
function (string $carry, ReflectionProperty $property) use ($isOptional, $missingSymbols) {
function (string $carry, ReflectionProperty $property) use ($isClassOptional, $missingSymbols, $nullablesAreOptional) {
$isHidden = ! empty($property->getAttributes(Hidden::class));

if ($isHidden) {
return $carry;
}

$isOptional = $isOptional || ! empty($property->getAttributes(Optional::class));
$isOptional = $isClassOptional
|| ! empty($property->getAttributes(Optional::class))
|| ($property->getType()?->allowsNull() && $nullablesAreOptional);

$transformed = $this->reflectionToTypeScript(
$property,
$missingSymbols,
$isOptional,
...$this->typeProcessors()
);

Expand Down
2 changes: 2 additions & 0 deletions src/Transformers/InterfaceTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function (string $parameterCarry, \ReflectionParameter $parameter) use ($missing
$type = $this->reflectionToTypeScript(
$parameter,
$missingSymbols,
false,
...$this->typeProcessors()
);

Expand All @@ -53,6 +54,7 @@ function (string $parameterCarry, \ReflectionParameter $parameter) use ($missing
$returnType = $this->reflectionToTypeScript(
$method,
$missingSymbols,
false,
...$this->typeProcessors()
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/Transformers/TransformsTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ trait TransformsTypes
protected function reflectionToTypeScript(
ReflectionMethod | ReflectionProperty | ReflectionParameter $reflection,
MissingSymbolsCollection $missingSymbolsCollection,
bool $nullablesAreOptional = false,
TypeProcessor ...$typeProcessors
): ?string {
$type = $this->reflectionToType(
Expand All @@ -31,6 +32,7 @@ protected function reflectionToTypeScript(
return $this->typeToTypeScript(
$type,
$missingSymbolsCollection,
$nullablesAreOptional,
$reflection->getDeclaringClass()?->getName()
);
}
Expand Down Expand Up @@ -60,10 +62,12 @@ protected function reflectionToType(
protected function typeToTypeScript(
Type $type,
MissingSymbolsCollection $missingSymbolsCollection,
bool $nullablesAreOptional = false,
?string $currentClass = null,
): string {
$transpiler = new TranspileTypeToTypeScriptAction(
$missingSymbolsCollection,
$nullablesAreOptional,
$currentClass,
);

Expand Down
14 changes: 14 additions & 0 deletions src/TypeScriptTransformerConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class TypeScriptTransformerConfig

private bool $transformToNativeEnums = false;

private bool $nullToOptional = false;

public static function create(): self
{
return new self();
Expand Down Expand Up @@ -90,6 +92,13 @@ public function transformToNativeEnums(bool $transformToNativeEnums = true): sel
return $this;
}

public function nullToOptional(bool $nullToOptional = false): self
{
$this->nullToOptional = $nullToOptional;

return $this;
}

public function getAutoDiscoverTypesPaths(): array
{
return $this->autoDiscoverTypesPaths;
Expand Down Expand Up @@ -162,4 +171,9 @@ public function shouldTransformToNativeEnums(): bool
{
return $this->transformToNativeEnums;
}

public function shouldConsiderNullAsOptional(): bool
{
return $this->nullToOptional;
}
}
22 changes: 19 additions & 3 deletions tests/Actions/TranspileTypeToTypeScriptActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@
use phpDocumentor\Reflection\Types\Self_;
use phpDocumentor\Reflection\Types\Static_;
use phpDocumentor\Reflection\Types\This;
use function PHPUnit\Framework\assertContains;
use function PHPUnit\Framework\assertEquals;
use function Spatie\Snapshots\assertMatchesSnapshot;
use Spatie\TypeScriptTransformer\Actions\TranspileTypeToTypeScriptAction;
use Spatie\TypeScriptTransformer\Structures\MissingSymbolsCollection;
use Spatie\TypeScriptTransformer\Tests\FakeClasses\Enum\RegularEnum;
use Spatie\TypeScriptTransformer\Types\StructType;

use function PHPUnit\Framework\assertContains;
use function PHPUnit\Framework\assertEquals;
use function Spatie\Snapshots\assertMatchesSnapshot;

beforeEach(function () {
$this->missingSymbols = new MissingSymbolsCollection();

$this->typeResolver = new TypeResolver();

$this->action = new TranspileTypeToTypeScriptAction(
$this->missingSymbols,
false,
'fake_class'
);
});
Expand Down Expand Up @@ -62,3 +64,17 @@

expect($transformed)->toBe('string | number');
});

it('does not add nullable unions to optional properties', function () {
$action = new TranspileTypeToTypeScriptAction(
$this->missingSymbols,
true
);

$transformed = $action->execute(StructType::fromArray([
'a_string' => 'string',
'a_nullable_string' => '?string',
]));

assertEquals('{a_string:string;a_nullable_string:string;}', $transformed);
});
31 changes: 23 additions & 8 deletions tests/Transformers/DtoTransformerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
use function PHPUnit\Framework\assertEquals;
use function Spatie\Snapshots\assertMatchesSnapshot;
use function Spatie\Snapshots\assertMatchesTextSnapshot;
use Spatie\TypeScriptTransformer\Attributes\Hidden;
use Spatie\TypeScriptTransformer\Attributes\LiteralTypeScriptType;
use Spatie\TypeScriptTransformer\Attributes\Optional;
Expand All @@ -20,6 +17,10 @@
use Spatie\TypeScriptTransformer\TypeProcessors\TypeProcessor;
use Spatie\TypeScriptTransformer\TypeScriptTransformerConfig;

use function PHPUnit\Framework\assertEquals;
use function Spatie\Snapshots\assertMatchesSnapshot;
use function Spatie\Snapshots\assertMatchesTextSnapshot;

beforeEach(function () {
$config = TypeScriptTransformerConfig::create()
->defaultTypeReplacements([
Expand Down Expand Up @@ -48,10 +49,10 @@
it('a type processor can remove properties', function () {
$config = TypeScriptTransformerConfig::create();

$transformer = new class($config) extends DtoTransformer {
$transformer = new class ($config) extends DtoTransformer {
protected function typeProcessors(): array
{
$onlyStringPropertiesProcessor = new class implements TypeProcessor {
$onlyStringPropertiesProcessor = new class () implements TypeProcessor {
public function process(
Type $type,
ReflectionProperty | ReflectionParameter | ReflectionMethod $reflection,
Expand All @@ -74,7 +75,7 @@ public function process(
});

it('will take transform as typescript attributes into account', function () {
$class = new class {
$class = new class () {
#[TypeScriptType('int')]
public $int;

Expand Down Expand Up @@ -102,7 +103,7 @@ public function process(
});

it('transforms properties to optional ones when using optional attribute', function () {
$class = new class {
$class = new class () {
#[Optional]
public string $string;
};
Expand Down Expand Up @@ -133,7 +134,7 @@ class DummyOptionalDto


it('transforms properties to hidden ones when using hidden attribute', function () {
$class = new class() {
$class = new class () {
public string $visible;
#[Hidden]
public string $hidden;
Expand All @@ -146,3 +147,17 @@ class DummyOptionalDto

assertMatchesSnapshot($type->transformed);
});

it('transforms nullable properties to optional ones according to config', function () {
$class = new class () {
public ?string $string;
};

$config = TypeScriptTransformerConfig::create()->nullToOptional(true);
$type = (new DtoTransformer($config))->transform(
new ReflectionClass($class),
'Typed'
);

$this->assertMatchesSnapshot($type->transformed);
});
7 changes: 4 additions & 3 deletions tests/Transformers/InterfaceTransformerTest.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?php

use function PHPUnit\Framework\assertNotNull;
use function PHPUnit\Framework\assertNull;
use function Spatie\Snapshots\assertMatchesTextSnapshot;
use Spatie\TypeScriptTransformer\Tests\Fakes\FakeInterface;
use Spatie\TypeScriptTransformer\Transformers\InterfaceTransformer;
use Spatie\TypeScriptTransformer\TypeScriptTransformerConfig;

use function PHPUnit\Framework\assertNotNull;
use function PHPUnit\Framework\assertNull;
use function Spatie\Snapshots\assertMatchesTextSnapshot;

it('will only convert interfaces', function () {
$transformer = new InterfaceTransformer(
TypeScriptTransformerConfig::create()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
string?: string;
}

0 comments on commit 2a81539

Please sign in to comment.