Attributes API Reference
Typographos uses PHP attributes to control type generation behavior.
TypeScript Attribute
#[TypeScript]
Marks a PHP class or enum for TypeScript type generation.
use Typographos\Attributes\TypeScript;
#[TypeScript]
class User
{
public function __construct(
public string $name,
public int $age,
) {}
}
- Must be applied to classes or enums
- Only classes/enums with this attribute are discovered during auto-discovery
- No parameters required
Supported Constructs
Classes
#[TypeScript]
class User { /* ... */ }
Enums (Backed Only)
#[TypeScript]
enum Status: string
{
case PENDING = 'pending';
case ACTIVE = 'active';
}
Not Supported
- Interfaces (use classes instead)
- Traits
- Pure enums (without backing type)
- Functions or constants
Inline Attribute
#[Inline]
Forces a type to be inlined instead of creating a reference.
use Typographos\Attributes\Inline;
use Typographos\Attributes\TypeScript;
#[TypeScript]
class User
{
public function __construct(
public string $name,
#[Inline] // Embed Address structure directly
public Address $address,
) {}
}
- Applied to class properties
- Works with class types and enum types
- Overrides global enum/record style settings
#[Inline]
:
export interface User {
name: string
address: Address // Reference
}
export interface Address {
street: string
city: string
}
#[Inline]
:
export interface User {
name: string
address: { // Inlined structure
street: string
city: string
}
}
export interface Address { // Still generated for other references
street: string
city: string
}
Inline with Enums
#[TypeScript]
enum Status: string
{
case PENDING = 'pending';
case ACTIVE = 'active';
}
#[TypeScript]
class Task
{
public function __construct(
public string $title,
#[Inline] // Force union type regardless of global enum style
public Status $status,
) {}
}
EnumStyle::ENUMS
):
export enum Status {
PENDING = "pending",
ACTIVE = "active",
}
export interface Task {
title: string
status: "pending" | "active" // Inlined as union type
}
Inline with Arrays
#[TypeScript]
class User
{
public function __construct(
public string $name,
/** @var list<Address> */
#[Inline] // Inline each Address in the array
public array $addresses,
) {}
}
export interface User {
name: string
addresses: {
street: string
city: string
}[] // Array of inlined Address structures
}
Attribute Positioning
Property Attributes
Attributes on properties control how that specific property is handled:
#[TypeScript]
class User
{
public function __construct(
public string $name, // Default behavior
#[Inline]
public Address $address, // This property inlined
public Address $billing, // This property referenced
) {}
}
Constructor Parameter Attributes
Attributes work on constructor parameters with property promotion:
#[TypeScript]
class User
{
public function __construct(
public string $name,
#[Inline]
public Address $address, // Attribute applies to the property
) {}
}
Attribute Inheritance
Attributes are not inherited. Each class must have its own #[TypeScript]
attribute:
#[TypeScript]
class User
{
// User implementation
}
// This class needs its own #[TypeScript] attribute
class AdminUser extends User
{
// AdminUser implementation
}
// To include AdminUser in generation:
#[TypeScript]
class AdminUser extends User
{
// Now AdminUser will be discovered
}
Error Conditions
Missing TypeScript Attribute
class User // No #[TypeScript] attribute
{
public function __construct(
public string $name,
) {}
}
$generator->generate([User::class]); // Will throw exception
Error: Class must have #[TypeScript]
attribute
Solution: Add the attribute:
#[TypeScript]
class User { /* ... */ }
Inline on Unsupported Types
#[TypeScript]
class User
{
public function __construct(
#[Inline]
public string $name, // Can't inline primitive types
) {}
}
Behavior: #[Inline]
is ignored for primitive types
Inline on Array Without PHPDoc
#[TypeScript]
class User
{
public function __construct(
#[Inline]
public array $data, // No PHPDoc for array
) {}
}
Error: Missing PHPDoc for array property
Solution: Add PHPDoc:
/** @var list<Address> */
#[Inline]
public array $addresses,
Best Practices
Use TypeScript Attribute Sparingly
Only mark classes that you actually need as TypeScript types:
// ✅ Good - Only DTO classes need TypeScript
namespace App\DTO;
#[TypeScript]
class UserCreateRequest { /* ... */ }
#[TypeScript]
class UserResponse { /* ... */ }
// ✅ Good - Don't mark internal classes
namespace App\Services;
class UserService // No #[TypeScript] - internal class
{
// Internal service logic
}
Use Inline for Simple Value Objects
// ✅ Good - Simple value object
#[TypeScript]
class Coordinates
{
public function __construct(
public float $lat,
public float $lng,
) {}
}
#[TypeScript]
class Location
{
public function __construct(
public string $name,
#[Inline] // Simple, single-use value object
public Coordinates $coords,
) {}
}
Avoid Inline for Complex Objects
// ❌ Avoid - Complex object with many properties
#[TypeScript]
class User
{
public function __construct(
public string $name,
public Address $address, // Keep as reference for reuse
) {}
}
Consistent Inline Strategy
// ✅ Good - Consistent strategy
#[TypeScript]
class Order
{
public function __construct(
// Inline simple value objects
#[Inline] public Money $total,
#[Inline] public Coordinates $location,
// Reference complex entities
public User $customer,
public Product $product,
) {}
}
Framework Integration
Validation Attributes
Typographos attributes can coexist with validation attributes:
use Symfony\Component\Validator\Constraints as Assert;
use Typographos\Attributes\TypeScript;
#[TypeScript]
class ContactForm
{
public function __construct(
#[Assert\NotBlank]
#[Assert\Email]
public string $email,
#[Assert\NotBlank]
public string $message,
) {}
}
Both Symfony validation attributes and Typographos attributes work together on the same class.