Классы, объекты и прототипы, о боже!

Объектно-ориентированное программирование в JavaScript всегда было немного странным, и, учитывая, что TypeScript является надмножеством JavaScript, некоторые из этих фанк все еще витают в воздухе.

Система прототипов для управления классами может показаться странной, если она исходит из других объектно-ориентированных языков, таких как Java, C # или C ++. В JavaScript класс фактически создает объект в памяти, как и любой другой объект, чтобы содержать общие функции, которые разделяют все экземпляры этого класса (обычно методы). Этот объект является прототипом, доступ к которому осуществляется через свойство prototype из класса и свойство __proto__ из всех экземпляров.

Когда мы создаем экземпляр объекта класса, мы фактически создаем новый пустой объект, но этот пустой объект имеет прототип, который указывает на общий экземпляр, поэтому наследует его поведение. Выполняется функция конструктора, которая обычно заполняет пустой объект свойствами.

Раньше это было более заметно, когда мы фактически писали классы как функции и манипулировали прототипом функции / класса.

Конечно, начиная с ECMAScript 2015 (ES6) у нас есть правильный синтаксис класса:

и этот синтаксис переносится на TypeScript:

Но поймите, что основное поведение одинаково с функциями конструктора и объектами-прототипами.

Специальные возможности, свойства и свойства параметров

Обратите внимание, что в нашем классе Circle в TypeScript мы должны были определить свойство radius. Это свойство можно настроить как частное, защищенное или общедоступное (по умолчанию публичное). Эти модификаторы доступности также применяются к методам.

В конструкторе, как это часто бывает, мы инициализируем свойство radius параметром, переданным в конструктор. Это можно упростить, добавив к параметру конструктора модификатор доступности, сделав его свойством параметра. Это устраняет необходимость в объявлении свойств и инициализации.

Мы также можем квалифицировать свойства как readonly. Это также работает для создания свойств параметров.

Наконец, мы также можем инициализировать свойства вне конструктора. Даже используя другие свойства и методы.

Наследование

Цепочки прототипов поддерживают объектно-ориентированное наследование, которое легко реализуется с помощью extends.

Что в типе

Ключевое понятие, которое следует помнить при работе с классами в TypeScript, заключается в том, что объект является экземпляром класса из-за его прототипа.

Однако, когда мы десериализуем из JSON, мы воссоздаем объекты, которые не имеют ссылок на какие-либо классы, которые мы написали. Они не будут отображаться как экземпляры класса, который использовался для их первоначального создания перед сериализацией, и у них не будет никаких методов.

Это верно, даже когда мы включаем утверждение типа. Поймите, что утверждения типа - это не приведение типов; мы только утверждаем. Мы информируем компилятор о том, что он не может сделать для себя. Обычно это происходит на границе ввода-вывода или при вызове нетипизированного JavaScript. Однако мы можем предоставить неверную информацию - например, заявить, что базовый объект является экземпляром класса.

Вот почему при передаче данных часто лучше использовать интерфейсы без методов. С TypeScript вы можете обнаружить, что по этой причине вы склоняетесь к более функционально-ориентированному подходу, используя базовые объекты, привязанные к интерфейсу, и бесплатные функции, которые принимают эти объекты в качестве входных данных.

Последние функции

Аксессоры с преобразованием

Аксессоры полезны для предоставления интерфейса, который используется как свойство, но с более сложной логикой под капотом. Используя get и / или set, мы можем привязать свойство к функциям получения и / или установки. Это полезно, например, при создании вычисляемых свойств.

Здесь diameter выглядит и ощущается как свойство для потребителя, но на самом деле это вызов функций, которые читают и записывают radius.

TypeScript 4.3 добавляет поддержку того, что тип установщика больше, чем тип получателя. Это может быть полезно для поддержки записи нескольких типов данных, которые в конечном итоге будут преобразованы в канонический тип. Например, ниже мы можем записать diameter как число или строку, но мы всегда читаем его как число.

Хотя вы не захотите делать это везде, это полезно для обертывания существующих API с аналогичным поведением. В 4.3 мы также получили поддержку для указания отдельных типов геттеров и сеттеров в интерфейсе.

Единственное ограничение состоит в том, что тип получателя должен быть назначенным типу установщика.

Истинные частные члены

Стоит отметить, что, как и многие вещи в TypeScript, модификаторы private и protected применяются только во время компиляции. Они помогают и защищают разработчика. Во время выполнения, когда все было преобразовано в JavaScript, все свойства будут существовать в объекте и будут доступны. Это включает в себя сериализацию объектов в JSON.

Сериализуется как:

{
    "key": "123",
    "id": "abc"
}

Чтобы обеспечить настоящую конфиденциальность, TypeScript добавил поддержку Частных полей ECMAScript в версии 3.8. Это всего лишь предложение стадии 3 (на момент написания) для JavaScript, но оно является частью языка TypeScript, начиная с 3.8. Эти поля имеют префикс #.

Это сохраняет значение для полей / свойств, внешних по отношению к объекту, поэтому, когда мы сериализуем частные поля, не присутствуют. Итак, экземпляр этого Secrets класса сериализуется как:

{}

Компиляция этого в более старый JavaScript интересна - он создает уровень модуля WeakMap для каждого поля, и объект должен искать значение своего поля в этой структуре, используя себя в качестве ключа.

TypeScript 4.3 расширяет частную поддержку ECMAScript, включая методы и средства доступа.

Ясность в отмене

До TypeScript 4.3 при переопределении методов в подклассе вы просто использовали то же имя. Это могло привести к незначительным ошибкам, если базовый класс изменился, но подклассы не были обновлены. Например, когда был удален метод базового класса.

TypeScript 4.3 вводит ключевое слово override для явной пометки методов как переопределенных. Если нет подходящей базовой записи для переопределения, то помечается ошибка. Это также лучше передает намерение читателя.

Чтобы избежать критических изменений, новый переключатель компилятора --noImplicitOverride отмечает ошибки, когда метод переопределяет базовый метод без этого ключевого слова.

Заключение

Надеюсь, это был полезный обзор объектно-ориентированной поддержки в TypeScript. Как видите, TypeScript 4.3 добавляет несколько полезных функций для этой парадигмы.

Если вы еще этого не сделали, ознакомьтесь с моими сообщениями, в которых показаны некоторые полезные функции TypeScript 4.1 и TypeScript 4.2.

Также обязательно ознакомьтесь с нашим курсом TypeScript. Мы также рады предоставить обучение Angular и React с использованием TypeScript. Мы доставляем виртуальные услуги компаниям по всему миру и будем рады адаптировать наши курсы к уровню вашей команды и конкретным потребностям. Приходите и проверьте нас, чтобы узнать, можем ли мы помочь вам и вашей команде.

Первоначально размещено здесь.