Type Narrowing

Type narrowing is the general process of refining the type of a value to a more specific subtype within a scope, based on information gathered from a preceding check: a type test, a comparison, an assignment, or a control-flow branch. It is a property of the type system or of the analysis performed on top of it, not of any single language feature.

Type narrowing shows up under many names depending on where it is enforced: TypeScript calls it control flow analysis, Kotlin calls the compiler-enforced form a smart cast, Flow and mypy narrow types across isinstance/typeof guards, and Rust’s borrow checker narrows Option<T> after a match arm destructures it. In all these systems the compiler tracks, statement by statement, which subtype a variable is guaranteed to hold, and rejects code that contradicts that guarantee.

PHP’s engine performs no static narrowing: types are checked at runtime, not tracked through control flow at compile time. Narrowing code is therefore always the work of an external tool:

  • Some static analysis tools narrow variables inside instanceof, is_string()/is_int()/etc., isset(), array_key_exists(), and match/switch branches, purely for static-analysis purposes.

  • Custom assertions, @psalm-assert and @phpstan-assert annotations, extend narrowing to user-defined predicate functions.

  • None of this narrowing is enforced by the PHP runtime; a value can still be used inconsistently with the narrowed type and only fail, if at all, when the offending operation actually executes.

smart-cast names the specific compiler-enforced flavor of narrowing found in languages like Kotlin and TypeScript; type narrowing is the broader concept those features are instances of.

<?php

    function describe(int|string $value): string {
        if (is_int($value)) {
            // Static analysers narrow $value to int in this branch;
            // the PHP engine performs no such tracking.
            return "int: " . ($value + 1);
        }

        // Narrowed to string here, again only for the analyser's benefit.
        return "string: " . strtoupper($value);
    }

?>

Documentation

See also TypeScript type narrowing, PHPStan type narrowing and Psalm – Typing in Psalm.

Related : Smart Cast, Type Widening, instanceof, Type System, Type Checking, Gradual Type Checking, Union Type, Static Code Analysis (SCA)