The package provides a DtoTransformer out of the box. This transformer will convert all public non-static properties of a class to TypeScript types.
Let's take a look at an example:
class User
{
public int $id;
public string $name;
public ?string $address;
public array $emails;
}
This will be transformed to:
export type User = {
int: number;
name: string;
address: string | null;
array: Array<string>;
}
In the next chapters, we'll look at how to adapt the DtoTransformer to be a perfect match for your project.
A small side note for the Laravel developers: you should use the DtoTransformer from the spatie/laravel-typescript-transformer package. This transformer has some extra niceties for Laravel projects you don't want to miss!
##Replacing simple types
In the end, the package can only output types that are string, bool, int, double, array, and references to other types (for example, another DTO). You will have to replace some types in your DTOs with more primitive versions. This is normal behavior since you can only communicate these primitive types with the frontend through JSON.
For example you cannot send a DateTime object to the frontend, you will probably be sending a string representation of the DateTime object.
Although it is possible to take full control of the DtoTransformer and you can change the behavior of the property type replacements to primitive types to your own liking. Sometimes you want to replace some simple types to TypeScript. For example, a DateTime or Carbon object will probably always be a string in your TypeScript definition.
It is possible to define these simple replacements in the config:
TypeScriptTransformerConfig::create()
->classPropertyReplacements([
DateTime::class => 'string',
])
...
In the above example, we replace all DateTimes with a string type. Any type you can use as a class property's @var annotation can be used as a replacement.
For example, should you want to represent a DateTime as an array of strings, then that's perfectly possible:
TypeScriptTransformerConfig::create()
->classPropertyReplacements([
DateTime::class => 'string[]',
])
...
If you want to convert to a literal/specific TypeScript type you can use the TypeScriptType class like this:
TypeScriptTransformerConfig::create()
->classPropertyReplacements([
DateTime::class => TypeScriptType::create('SomeGenericType<string>'),
])
...
##Writing your own DTO Transformers
Sometimes you'll want even more flexibility. For example when adding static properties to the transformed properties or when replacing properties with primitive types. In these cases you can create your own DtoTransformer:
class DtoTransformer extends DtoTransformer
{
public function canTransform(ReflectionClass $class): bool
{
return is_subclass_of($class->getName(), DataTransferObject::class);
}
protected function resolveProperties(ReflectionClass $class): array
{
return array_values($class->getProperties(ReflectionProperty::IS_PUBLIC));
}
protected function getClassPropertyProcessors(): array
{
return [
new ReplaceDefaultTypesClassPropertyProcessor(
$this->config->getClassPropertyReplacements()
),
new DtoCollectionClassPropertyProcessor(),
new ApplyNeverClassPropertyProcessor(),
];
}
}
First of all, canTransform still returns true if a transformer can handle the type conversion. In this specific example, we created a transformer that will only transform DTOs from our spatie/data-transfer-object package. We've also overwritten the resolveProperties method to not exclude static properties.
As you can see, we're defining some ClassPropertyProcessors. These processors will be used to replace the type of a property with a more primitive version and will run before any missing symbols are replaced. You can read more about class property processors here.
When creating your own DtoTransformer, do not forget to register it in your configuration!