Here’s a PHP gotcha that’s bitten me more than once when working with typed properties and external data sources like API responses or deserialized objects.
In PHP 7.4+, when you declare a typed property like this:
class ApiResponse
{
public ResponseData $data;
}
You might assume that $data defaults to null if it’s never assigned. It doesn’t. It’s in an uninitialized state — which is different from null. Try to access it and you’ll get:
TypeError: Typed property ApiResponse::$data must not be accessed before initialization
This is especially common when deserializing API responses. If the external service returns a malformed payload missing expected fields, your deserializer creates the object but never sets the property. PHP then explodes when you try to read it.
The fix is straightforward — make the property nullable and give it a default:
class ApiResponse
{
public ?ResponseData $data = null;
}
Two things changed: the ? prefix makes the type nullable, and = null provides an explicit default. Both are required — even a ?Type property without = null stays uninitialized.
Then add a null-safe check where you access it:
if (!$response->data?->items) {
// Handle missing data gracefully
return [];
}
The ?-> operator (PHP 8.0+) short-circuits to null instead of throwing an error. Clean and defensive.
The takeaway: Any typed property that might not get initialized — especially in DTOs, API response objects, or anything populated by external data — should be nullable with an explicit = null default. Don’t assume your data sources will always send complete payloads.

Leave a Reply