В классах обязательства по контрактам берут на себя методы:
<?php
class A {}
class B extends A {}
function foo(A $a) {}
function bar(B $b)
{
    foo($b);
}
?>Приведённый пример кода безопасен с точки зрения типов, поскольку класс B следует контракту класса A. Следование одного класса контракту другого порождает магию ко- и контравариантности. Поэтому ожидания, которые возникают в отношении методов, сохранятся, кроме исключений.
В перечислениях обязательства по контрактам берут на себя варианты, а не методы:
<?php
enum ErrorCode
{
    case SOMETHING_BROKE;
}
function quux(ErrorCode $errorCode)
{
    // Кажется, что код охватывает каждый вариант перечисления
    match ($errorCode) {
        ErrorCode::SOMETHING_BROKE => true,
    };
}
?>
   Статический анализ выражения match в функции quux
   показывает, что проверяется каждый вариант перечисления ErrorCode.
  
Но представьте, что перечисления разрешили бы расширять:
<?php
// Код мысленного эксперимента, в котором перечисления не окончательны.
// Обратите внимание, что этот код не сработает в PHP.
enum MoreErrorCode extends ErrorCode
{
    case PEBKAC;
}
function fot(MoreErrorCode $errorCode)
{
    quux($errorCode);
}
fot(MoreErrorCode::PEBKAC);
?>По стандартным правилам наследования класс-наследник пройдёт проверку типа.
   Проблема состояла бы в том, что выражение match в функции quux() уже не покрывало бы каждый
   вариант перечисления. И поскольку выражение проверки не знает о варианте MoreErrorCode::PEBKAC,
   сопоставление выбросит исключение.
  
Поэтому перечисления окончательны и их нельзя расширять.
