Tip
DO declare only public and protected members. Private trait members are not accessible from outside and should be omitted.
Tier 0 ยท Story 02Complete
Tyhpdef lets you describe existing PHP traits to the Tyhp compiler. Traits in PHP provide a mechanism for code reuse across classes. When importing a trait, you declare the typed signatures of its public and protected members โ methods, properties, and constants โ so that Tyhp can type-check code that uses the trait.
Consider a PHP trait you want to use from Tyhp:
<?php
trait TimestampedEntity {
protected $createdAt;
protected $updatedAt;
public function touch(): void {
$this->updatedAt = new DateTime();
}
abstract public function save(): void;
}
The Tyhpdef declaration adds full type information. Methods end with semicolons โ no bodies:
<?tyhpdef
trait TimestampedEntity {
protected \DateTime $createdAt;
protected \DateTime $updatedAt;
public function touch(): void;
abstract public function save(): void;
}
Trait properties are declared with visibility modifiers and types, just like class properties.
<?tyhpdef
trait HasSlug {
public string $slug;
protected ?string $slugSource;
public function generateSlug(): string;
public function getSlugAttribute(): string;
}
Traits can declare typed constants.
<?tyhpdef
trait Configurable {
public const string CONFIG_KEY;
public const int DEFAULT_TIMEOUT;
public function getConfig(string $key): mixed;
public function setConfig(string $key, mixed $value): void;
}
Traits can require using classes to implement specific methods by declaring them as abstract.
<?tyhpdef
trait Validatable {
abstract public function validate(): bool;
abstract protected function getRules(): array<string, string>;
public function isValid(): bool;
public function getErrors(): array<string>;
}
Traits support generic type parameters. When a class uses a generic trait, it must provide concrete type arguments.
<?tyhpdef
trait Collection<T> {
public function add(T $item): void;
public function remove(T $item): bool;
public function contains(T $item): bool;
public function getItems(): array<T>;
public function count(): int;
}
trait KeyValueStore<TKey extends string|int, TValue> {
public function get(TKey $key): ?TValue;
public function set(TKey $key, TValue $value): void;
public function has(TKey $key): bool;
}
Once a trait is declared in Tyhpdef, Tyhp classes can use it with full type checking. Generic traits require concrete type arguments at the use site.
<?tyhp
class BlogPost {
use TimestampedEntity;
use HasSlug;
public function save(): void {
$this->touch();
// ... save implementation
}
}
class UserCollection {
use Collection<User>;
public function getActiveUsers(): array<User> {
return \array_filter(
$this->getItems(),
fn(User $u): bool => $u->isActive()
);
}
}
When a class uses multiple traits that have methods with the same name, you can use PHP's conflict resolution syntax (insteadof and as) in your Tyhp code. The imported traits will carry their method signatures so Tyhp can validate the resolution.
<?tyhp
class AuditableUser {
use TimestampedEntity, SoftDeletes {
SoftDeletes::delete insteadof TimestampedEntity;
TimestampedEntity::delete as hardDelete;
}
}
The trait itself can be marked deprecated or obsolete (top-level). Member-level markers parse but are not enforced in this alpha.
<?tyhpdef
deprecated trait LegacyCacheable {
public function cacheFor(int $seconds): void;
}
trait Cacheable {
public function cacheForever(): void;
public function cache(int $ttl): void;
public function invalidateCache(): void;
}
DO declare only public and protected members. Private trait members are not accessible from outside and should be omitted.
DO mark abstract methods as abstract in your Tyhpdef declaration. This ensures Tyhp enforces that using classes implement them.
DON'T include method bodies in trait declarations. All methods must end with a semicolon.
DON'T include property default values. Properties are declared as type and name only.