Модификатор override

Механизм наследования, особенно при написании так называемых библиотек, позволяет реализовать большую часть необходимого функционала в суперклассах, что значительно сокращает время разработки. Но при работе с наследованием можно встретить острые углы, сгладить которые в TypeScript предполагается за счёт оператора override опции компилятора --noImplicitOverride, которым и будет посвящена эта глава.

Модификатор override и флаг --noImplicitOverride

Представьте случай переопределения подклассом некоторых методов своего суперкласса.

ts
class SuperClass {
    /**
     * [*] Определяет метод
     */
    a(){} // [*]
    b(){}// [*]
}
class SubClass extends SuperClass {
    /**
     * [*] Переопределяет методы своего суперкласса.
     */
    a(){} // [*]
    b(){} // [*]
}

Всё банально просто! Но что, если над проектом работает большое количество команд находящихся в разных уголках земного шара и команде занимающейся разработкой SuperClass неожиданно придеёт в голову изменить его api удалив оба метода? В таком случае, разработчики класса SubClass даже не узнают об этом, поскольку переопределение превратится в определение. Другими словами, компилятор даже глазом не моргнет, поскольку посчитает, что класс SubClass определят методы a() и b().

ts
class SuperClass {
    /**
     * Удалили a() и b() и добавили c().
     */
    с(){}
}
class SubClass extends SuperClass {
    /**
     * Ошибки не возникает, так как компилятор считает
     * что данный класс определяет оба метода.
     */
    a(){}
    b(){}
}

Для предотвращения подобных сценариев, в TypeScript существует модификатор override, который однозначно указывает на переопределение методов родительского класса. При использовании модификатора override компилятор сможет понять, что происходит переопределение несуществующих в суперклассе методов и сообщить об этом с помощью ошибки.

ts
class SuperClass {
    /**
     * Удалили a() и b() и добавили c().
     */
    с(){}
}
class SubClass extends SuperClass {
    /**
     * [*] Error -> 
     * This member cannot have an 'override' modifier
     * because it is not declared in the base class 'SuperClass'.ts(4113)
     * 
     * Теперь компилятор понимает, что происходи переопределение
     * несуществующих методов.
     */
    override a(){} // [*]
    override b(){} // [*]
}

Также при использовании механизма наследования может возникнуть диаметрально противоположная ситуация, при которой суперкласс может объявить метод, который уже существует в его потомке.

ts
/**
 * [0] метод определенный только в SubClass
 */
class SuperClass {
    a(){}
}
class SubClass extends SuperClass {
    b(){} // [0]
}

/**
 * [1] Но спустя некоторое время класс SuperClass
 * определяет метод b(), который уже существует в
 * классе-потомке [2]. Другими словами, произошло
 * нежелаемое переопределение способное привести
 * к непредсказуемому поведению программы.
 */
class SuperClass {
    a(){}
    b(){} // [1]
}
class SubClass extends SuperClass {
    b(){} // [2]
}

Предотвратить нежелательное поведение в TypeScript можно с помощью флага --noImplicitOverride, при активации которого, в подобных случаях будет возникать ошибка.

ts
class SuperClass {
    a(){}
    b(){}
}
class SubClass extends SuperClass {
    /**
     * --noImplicitOverride = true
     * 
     * [*] Error -> This member must have an 'override'
     * modifier because it overrides a member in the base
     * class 'SuperClass'.ts(4114)
     */
    b(){} // [*]
}