Type Aliases
TypePHP supports declaring local type aliases (@phpstan-type / @psalm-type) and importing type aliases from other classes (@phpstan-import-type / @psalm-import-type). This allows you to centralize and reuse complex array shapes, unions, and generic structures across your application.
Tooling Compatibility: Both PHPStan syntax (
@phpstan-type,@phpstan-import-type) and Psalm syntax (@psalm-type,@psalm-import-type) are parsed identically and enforced at runtime.
Local Type Aliases (@phpstan-type / @psalm-type)
Declare a local type alias above a class or interface definition using @phpstan-type or @psalm-type. Once declared, you can reference the alias in any parameter, return, or @var docblock within that class:
<?php
declare(strict_types=1);
namespace App\Services;
/**
* Declare local type aliases for this class
*
* @phpstan-type UserShape array{id: positive-int, username: non-empty-string, role: 'admin'|'editor'|'viewer'}
* @psalm-type UserStatus 'active'|'pending'|'archived'
*/
class UserService
{
/**
* @param UserShape $user
* @param UserStatus $status
*/
public function updateUser(array $user, string $status): bool
{
return true;
}
}
$service = new UserService();
// Valid Call
$service->updateUser(['id' => 10, 'username' => 'Alice', 'role' => 'admin'], 'active');
// Invalid Call ($id is negative, violating UserShape)
$service->updateUser(['id' => -5, 'username' => 'Alice', 'role' => 'admin'], 'active');
// Throws: TypeError: UserService::updateUser(): Argument $user['id'] must be of type positive-intImported Type Aliases (@phpstan-import-type / @psalm-import-type)
To share type aliases across multiple classes, declare your aliases in a central class (e.g. GlobalTypes) and import them into other classes using @phpstan-import-type or @psalm-import-type:
Central Type Definitions (GlobalTypes.php)
namespace App\Types;
/**
* Shared Type Definitions
*
* @phpstan-type SharedUserShape array{id: positive-int, email: non-empty-string}
* @psalm-type SharedRole 'admin'|'user'
*/
class GlobalTypes
{
}Importing the Shared Type Alias (UserApi.php)
namespace App\Api;
use App\Types\GlobalTypes;
/**
* Import shared types from GlobalTypes
*
* @phpstan-import-type SharedUserShape from GlobalTypes
* @psalm-import-type SharedRole from GlobalTypes
*/
class UserApi
{
/**
* @param SharedUserShape $user
* @param SharedRole $role
*/
public function saveUser(array $user, string $role): bool
{
return true;
}
}
$api = new UserApi();
// Valid Call
$api->saveUser(['id' => 42, 'email' => 'alice@example.com'], 'admin');
// Invalid Call ($email is empty string)
$api->saveUser(['id' => 42, 'email' => ''], 'admin');
// Throws: TypeError: UserApi::saveUser(): Argument $user['email'] must be of type non-empty-stringImporting with Local Alias Renaming (as)
Use the as keyword to rename an imported type alias locally to prevent naming collisions or improve local code clarity:
namespace App\Services;
use App\Types\GlobalTypes;
/**
* Import and rename the shared type alias
*
* @phpstan-import-type SharedUserShape from GlobalTypes as LocalUserShape
*/
class AccountService
{
/**
* @param LocalUserShape $payload
*/
public function createAccount(array $payload): void
{
// ...
}
}Naming Collisions (When as is Omitted)
If a class defines a local @phpstan-type Status AND imports a type alias with the exact same name (@phpstan-import-type Status from GlobalTypes) without using the as keyword:
- Resolution Priority: The imported type alias will overwrite the local type alias.
- Best Practice: Always use the
askeyword whenever an imported alias name collides with a local alias name to make your type contracts explicit:
/**
* Local alias: 'active'|'pending'
* @phpstan-type Status 'active'|'pending'
*
* Imported alias renamed to GlobalStatus to prevent overwriting local 'Status'
* @phpstan-import-type Status from GlobalTypes as GlobalStatus
*/
class OrderService
{
// ...
}Chained Type Alias Imports
TypePHP recursively resolves multi-level type alias import chains down to the root definition:
- Level 1 (
GlobalTypes): Defines@phpstan-type UserShape array{id: positive-int}. - Level 2 (
MidService): Imports@phpstan-import-type UserShape from GlobalTypes. - Level 3 (
FinalService): Imports@phpstan-import-type UserShape from MidService as LocalShape.
When FinalService validates $payload against LocalShape, TypePHP automatically follows the 3-class import chain back to GlobalTypes and enforces array{id: positive-int}!
$service = new FinalService();
// Valid Call
$service->process(['id' => 100]);
// Invalid Call (id is negative)
$service->process(['id' => -5]);
// Throws: TypeError: FinalService::process(): Argument $payload['id'] must be of type positive-int