TypeScript. Classes

5 min read

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

Свойства класса

В JavaScript нет возможности назначить свойства при объявлении класса — все необходимые значения нужно определять в конструкторе или других методах. При таком подходе объявление свойств неявное, не всегда ясно какие свойства имеет класс. TS решает эту проблему: здесь можно не только объявить свойства класса, но и назначить им начальные значения:

class Phone { brand: string; type: string = 'Smartphone'; constructor (brand: string, type?: string) { this.brand = brand; if (type) this.type = type; } }

Модификаторы доступа

Одним из недостатков ES6 классов является невозможность сделать методы и свойства приватными. В TS есть привычные модификаторы: public, private и protected, которые можно использовать как для методов, так и для свойств. По умолчанию, как и в других языках, все свойства имеют модификатор public:

class Phone { brand: string; type: string = 'Smartphone'; private serialNumber: number; constructor (brand: string, serialNumber: number, type?: string) { this.brand = brand; this.serialNumber = serialNumber; if (type) this.type = type; } }

Важно отметить, что наличие private или protected не означает приватность этих методов во внешнем коде. При попытке доступа к приватному свойству из TS кода компилятор выдаст ошибку, но после компиляции эти свойства все равно будут доступны из внешнего кода.

Readonly свойства

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

class Phone { readonly brand: string; readonly type: string = 'Smartphone'; constructor (brand: string, type?: string) { this.brand = brand; if (type) this.type = type; } }

Помимо объявления таких свойств стандартным образом, TS предоставляет возможность определять их в параметрах конструктора:

class Phone { readonly type: string = 'Smartphone'; constructor (readonly brand: string, type?: string) { if (type) this.type = type; } } const phone = new Phone('iPhone');

Переданные значения будут назначены в соответствующие свойства. Такая запись сокращает код, но делает объявление свойств менее явным.

Абстрактные классы

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

abstract class Phone { readonly type: string = 'Smartphone'; constructor (readonly brand: string, type?: string) { if (type) this.type = type; } call (name: string) { console.log(`Calling ${name}`); } abstract charge (charger: string); } class iPhone extends Phone { constructor () { super('Apple'); } charge (charger: string) { if (charger !== 'lightning') { throw Error('iPhone doesn\'t support this charger'); } } } class Pixel extends Phone { constructor () { super('Google'); } charge (charger: string) { if (charger !== 'USB-C') { throw Error('Pixel doesn\'t support this charger'); } } } const pixel = new Pixel(); pixel.charge('USB-C'); pixel.call('Mom')

Классы как интерфейсы

В предыдущей статье был рассмотрен случай наследования одного интерфейса от другого. Но TS позволяет также наследовать интерфейс от класса. Дело в том, что при объявлении класса создаются две сущности: тип, который описывает поля и методы, и функция-конструктор, которая создает объект класса. Поскольку в TypeScript применяется «утиная» типизация, интерфейсы по сути и являются типами данных. А значит можно расширить созданный при объявлении класса тип новым интерфейсом:

class Phone { readonly brand: brand; } interface AndroidPhone extends Phone { androidVersion: string; }

Не смотря на множество вариантов описания пользовательского типа данных, все они выполняют одну и ту же функцию. Использование того или иного способа остается на усмотрение разработчика. 

Коротко о главном

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