TypeError — When Type Declarations Throw
debt(d5/e3/b3/t6)
Closest to 'specialist tool catches' (d5), since phpstan/psalm detect type contract violations statically, but without them TypeErrors surface only at runtime when the boundary is hit.
Closest to 'simple parameterised fix' (e3), per quick_fix: add declare(strict_types=1) and adjust catch clauses to \TypeError/\Throwable — small pattern replacement within affected files.
Closest to 'localised tax' (b3), since type declarations and strict_types apply file-by-file and the error-handling pattern is localised, though it can reach across contexts (web/cli/queue).
Closest to 'serious trap' (t7), per misconception: developers believe TypeError only fires under strict_types and that it extends Exception — both wrong (it extends Error), so catch(Exception) silently misses it.
TL;DR
Explanation
In PHP 7+, type declarations throw TypeError when violated. With declare(strict_types=1), PHP enforces types strictly at call sites. Without it, PHP coerces where possible (int '5' → 5). TypeError extends Error (not Exception) — catch it separately or use Throwable. Common triggers: passing string to int parameter with strict_types, returning wrong type, violating union types. PHP 8.1 intersection types and DNF types (PHP 8.2) add more ways to throw TypeError.
Common Misconception
Why It Matters
Common Mistakes
- Catching Exception instead of Error or Throwable for TypeError.
- Not using strict_types=1 — coercion hides bugs.
- Returning null from a non-nullable return type — use ?Type or add null to union.
Code Examples
function add(int $a, int $b): int { return $a + $b; }
add('five', 2); // Without strict_types: coercion
// With strict_types=1: TypeError: add(): Argument #1 must be int, string given
declare(strict_types=1);
function add(int $a, int $b): int { return $a + $b; }
try {
add('five', 2);
} catch (\TypeError $e) {
throw new \InvalidArgumentException('Numeric values required', 0, $e);
}