Note
In tyhpdef, you do not need to specify a final encompassing signature that accepts all possible parameter types. The compiler constructs one internally by combining all declared overloads.
Tier 0 · Story 02Complete
Tyhpdef allows you to declare multiple signatures for the same function or method name. This is essential for describing PHP functions that accept different parameter types or counts and return different types depending on the arguments. In PHP, a single function like substr or json_decode handles multiple calling patterns internally. In tyhpdef, you describe each pattern as a separate overload signature so the Tyhp compiler can select the correct return type at each call site.
To declare overloads, simply declare the same function name multiple times with different parameter signatures. Each declaration must have a unique combination of parameter types. The compiler internally constructs a combined encompassing signature from all declared overloads.
<?tyhpdef
function \substr(string $string, int $offset): string;
function \substr(string $string, int $offset, int $length): string;
function \parse(int $value): IntegerResult;
function \parse(string $value): StringResult;
function \parse(array $value): ArrayResult;
In tyhpdef, you do not need to specify a final encompassing signature that accepts all possible parameter types. The compiler constructs one internally by combining all declared overloads.
Methods within class, interface, and trait declarations can also be overloaded. Each overload must have the same visibility modifier.
<?tyhpdef
class Repository {
public function find(int $id): ?User;
public function find(string $uuid): ?User;
public function find(array<string, mixed> $criteria): array<User>;
public function save(User $user): User;
public function save(array<User> $users): array<User>;
}
Literal values (static value types) can be used as parameter types in overloads. This allows the compiler to select different return types based on the literal value passed at a call site.
<?tyhpdef
function \getConfig(true $asArray): array<string, mixed>;
function \getConfig(false $asArray): string;
function \json_decode(string $json, true $associative): array<string, mixed>;
function \json_decode(string $json, false $associative): object;
function \json_decode(string $json, null $associative = null): mixed;
Overloaded functions can use generic type parameters. Each overload can have its own generic parameter set.
<?tyhpdef
function \array_map<T, U>(
callable<T, U> $callback,
array<T> $array
): array<U>;
function \array_map<T1, T2, U>(
callable<T1, T2, U> $callback,
array<T1> $array1,
array<T2> $array2
): array<U>;
function \array_map<T1, T2, T3, U>(
callable<T1, T2, T3, U> $callback,
array<T1> $array1,
array<T2> $array2,
array<T3> $array3
): array<U>;
When a function has async overloads, all overloads for that name must consistently use the async keyword.
<?tyhpdef
class HttpClient {
async public function request(string $url): Response;
async public function request(string $url, RequestOptions $options): Response;
async public function get(string $url): Response;
async public function post(string $url, mixed $body): Response;
}
The compiler resolves overloads at compile time in two stages:
If several candidates share the winning score, the first one wins. This alpha does not emit a dedicated overload-ambiguity diagnostic.
When implementing a class in Tyhp that has overloaded interface methods from tyhpdef, you only need to implement the single encompassing method that handles all overload variants.
<?tyhp
class UserRepository implements Repository {
public function find(int|string|array<string, mixed> $idOrUuidOrCriteria): null|User|array<User> {
if ($idOrUuidOrCriteria is int) {
return $this->findById($idOrUuidOrCriteria);
}
if ($idOrUuidOrCriteria is string) {
return $this->findByUuid($idOrUuidOrCriteria);
}
return $this->findByCriteria($idOrUuidOrCriteria);
}
}
Repository $repo = new UserRepository();
?User $user1 = $repo->find(123);
?User $user2 = $repo->find("abc-123");
array<User> $users = $repo->find(['status' => 'active']);
Top-level function overloads can be marked deprecated or obsolete independently. Member-level overload markers on class/interface methods parse but are not enforced in this alpha.
<?tyhpdef
deprecated function \connectDb(string $connectionString): DbConnection;
function \connectDb(DbConfig $config): DbConnection;
DO: Use overloads to describe PHP functions that behave differently based on parameter types or counts. This gives the Tyhp compiler precise return type information for each call pattern.
DO: Use static value types (like true, false, literal integers) in overloads when a PHP function returns different types based on a boolean or flag argument.
DON'T: Declare overloads with identical parameter type lists. Each overload must have a unique parameter signature — the compiler cannot distinguish between overloads that accept the same types.
DON'T: Mix async and non-async overloads for the same function name. If one overload is async, all overloads of that name must be async.
async keyword across all overloadsdeprecated or obsolete (member-level flags are not enforced yet)