Function, Functional Types¶
Функция — это ключевая концепция JavaScript. Функции присваиваются в качестве значений переменным и передаются как аргументы при вызове других функций. Поэтому не удивительно, что TypeScript очень много внимания уделяет возможностям функционального типа, к которым, начиная с текущей главы, повествование периодически будет возвращаться.
Function Types - тип функция¶
В TypeScript тип Function
представляет собой одноименный JavaScript конструктор, являющийся базовым для всех функций. Тип Function
можно указывать в аннотации типа тогда, когда о сигнатуре функции ничего неизвестно или в качестве значения могут выступать функции с несовместимыми сигнатурами.
1 2 3 4 5 6 7 8 9 10 |
|
При этом нельзя забывать, что по канонам статически типизированных языков, архитектуру программы нужно продумывать так, чтобы сводить присутствие высших в иерархии типов к нулю. В тех случаях, когда сигнатура функции известна, тип стоит конкретизировать при помощи определения более конкретных функциональных типов.
Поведение типа Function
идентично одноимённому типу из JavaScript.
Functional Types - функциональный тип¶
Помимо того, что в TypeScript существует объектный тип Function
, также существует функциональный тип, с помощью которого осуществляется описание сигнатур функциональных выражений.
Функциональный тип обозначается с помощью пары круглых скобок ()
, после которых располагается стрелка, а после неё обязательно указывается тип возвращаемого значения () => type
. При наличии у функционального выражения параметров, их декларация заключается между круглых скобок (p1: type, p2: type) => type
.
1 |
|
Если декларация сигнатуры функционального выражения известна, то рекомендуется использовать более конкретный функциональный тип, поскольку он в большей степени соответствует типизированной атмосфере.
1 2 3 4 |
|
Поведение функционального типа, указывающегося с помощью функционального литерала, идентично поведению типа Function
, но при этом оно более конкретно и поэтому предпочтительнее.
this в сигнатуре функции¶
Ни для кого не будет секретом, что в JavaScript при вызове функций можно указать их контекст. В львиной доле случаев, возможность изменять контекст вызова функции является нежелательным поведением JavaScript, но только не в случае реализации конструкции, называемой функциональная примесь (functional mixins).
Функциональная примесь — это функция, в теле которой происходит обращение к членам, объявленным в объекте, к которому она “примешивается”. Проблем не возникнет, если подобный механизм реализуется в динамически типизированном языке, каким является JavaScript.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Но в статически типизированном языке такое поведение должно быть расценено как ошибочное, поскольку у функции нет присущего объектам признака this
. Несмотря на это в JavaScript, а значит и в TypeScript, контекст самой программы (или, по другому, глобальный объект) является объектом. Это в свою очередь означает, что не существует места, в котором бы ключевое слово this
привело к возникновению ошибки (для запрещения this
в нежелательных местах нужно активировать опцию компилятора --noImplicitThis
). Но при этом за невозможностью предугадать поведение разработчика, в TypeScript ссылка this
вне конкретного объекта ссылается на тип any
, что лишает ide автодополнения. Для таких и не только случаев была реализована возможность декларировать тип this
непосредственно в функциях.
this
указывается в качестве первого параметра любой функции, и как обычный параметр имеет аннотацию типа, устанавливающую принадлежность к конкретному типу.
1 2 3 4 5 |
|
Несмотря на то, что this
декларируется в параметрах функции, таковыми оно не считается. Поведение функции с декларацией this
аналогично поведению функции без декларации this
. Единственное, на что стоит обратить внимание, что в случае указания принадлежности к типу, отличному от void
, не получится вызвать функцию вне указанного контекста.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Кроме того, возможность ограничивать поведение ключевого слова this
в теле функции призвано частично решить самую часто возникающую проблему, связанную с потерей контекста. Вряд ли найдется разработчик JavaScript, который может похвастаться, что ни разу не сталкивался с потерей контекста при передаче метода объекта в качестве функции обратного вызова (callback). В случаях, когда в теле метода происходит обращение через ссылку this
к членам объекта, в котором он определен, то при потере контекста, в лучшем случае возникнет ошибка. В худшем, предполагающем, что в новом контексте будут присутствовать схожие признаки, возникнет трудно выявляемая ошибка.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Для этих случаев TypeScript предлагает ограничить ссылку на контекст с помощью конкретизации типа ссылки this
.
Так как реальный пример, иллюстрирующий полную картину, получается очень объемным, то ограничимся одним методом, реализующим обсуждаемое поведение.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Стоит заметить, что одной конкретизации типа ссылки this
в слушателе событий недостаточно. Для того чтобы пример заработал должным образом, необходимо конкретизировать ссылку this
в самом слушателе событий.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
Также стоит обратить внимание на одну неочевидную на первый взгляд деталь. Когда мы передаем слушатель, обернув его в стрелочную функцию, либо в метод функции .bind
, ошибки не возникает только потому, что у передаваемой функции отсутствует декларация this
.