PHP Typed Properties: Declaration, Enforcement, and Inheritance Rules
Learn how to declare typed properties in PHP 7.4+, understand runtime type checks, inheritance constraints, and avoid common pitfalls.
16 Aug 2026, 10:49 UTC

Useful answer
PHP typed properties let you declare a class member with a specific type (int, string, bool, float, object, callable, iterable, array, or a class/interface name). The engine enforces that type at runtime, throwing a TypeError if you read an uninitialized property or assign a value of an incompatible type. This gives you early‑failure safety similar to static typing while keeping the language dynamic.
How typed properties work
When you declare a property with a type, PHP treats it as a normal variable but adds two checks:
- Assignment check – Whenever you write to the property, the runtime verifies that the value matches the declared type (or is
nullif the type is nullable). - Read check – When you read the property, PHP ensures it has been initialized; otherwise a
TypeErroris thrown.
These checks happen at execution time, not during compilation, so the script must actually reach the code path for the error to surface.
Worked example
The following snippet shows a class with several typed properties, including a nullable one, and demonstrates correct and incorrect usage.
<?php
class User {
// non‑nullable int property – must be set before reading
private int $id;
// nullable string property – can be null or a string
private ?string $email;
// object property typed to a class name
private DateTime $createdAt;
public function __construct(int $id, ?string $email = null) {
$this->id = $id; // OK: int matches int
$this->email = $email; // OK: null or string matches ?string
$this->createdAt = new DateTime(); // OK: DateTime matches DateTime
}
public function getId(): int {
return $this->id; // Safe: $id is guaranteed set
}
public function setEmail(?string $email): void {
$this->email = $email; // Assignment check runs here
}
}
// Correct usage
$user = new User(42, '[contact removed]');
echo $user->getId(); // prints 42
// Incorrect usage – trying to read before assignment (if we removed default)
class Example {
private int $counter;
}
$ex = new Example();
echo $ex->counter; // Throws TypeError: Typed property Example::$counter must not be accessed before initialization
?>
When you run the script, the first block executes without errors. The second block (the Example class) triggers a fatal TypeError because $counter is accessed before any value is assigned.
Limits and common mistakes
1. Uninitialized access
Accessing a typed property that has never been set throws a TypeError. The only ways to avoid this are:
- Give the property a default value that matches its type (e.g.,
private int $count = 0;). - Declare the type as nullable (
?int) and rely on the implicitnulldefault.
Failing to initialize leads to a hard fatal error, not a warning.
2. Default value restrictions
PHP only allows a default value when:
- The type is nullable (
?Type), in which case the default may benull. - The default value exactly matches the declared type (e.g.,
private string $name = 'default';).
Any other combination results in a compile‑time parse error.
3. Inheritance rules
Child classes may redeclare a property only if the new type is covariant (more specific) for object types, and visibility cannot be reduced. For scalar types, the type must stay identical.
Example of allowed covariance:
<?php
class Base {
private stdClass $obj;
}
class Derived extends Base {
// More specific type: child class of stdClass
private class extends stdClass $obj; // OK in PHP 8.0+, but illustrates the concept
}
?>
Attempting to widen the type (e.g., changing private Foo $prop; in the parent to private object $prop; in the child) violates Liskov substitution and will produce a TypeError if the child tries to assign a Foo instance.
4. Mixed types and callable/iterable
Types like callable and iterable are supported, but they behave like any other type: assigning an invalid value throws a TypeError. Remember that iterable accepts arrays or objects implementing Traversable.
Practical verification
To confirm that typed properties are working as expected in your environment:
- Run PHP 7.4 or newer (
php -v). - Create a script with a typed property, assign a correct value, and read it – the script should finish without errors.
- Modify the script to either read the property before assignment or assign a mismatched type; executing it should produce a
TypeError. - For inheritance, create a parent and child class where the child refines the property type; assign a value of the refined type and verify no error, then try assigning a value of the parent type and observe the error.
These steps let you see the enforcement in action without relying on any external test harness.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.