Хитрости TypeScript. Функции и лямбды

Есть ли разница в TypeScript между функцией и лямбдой? Что может произойти если заменить одну на другую? Давайте посмотрим.

Различие функции и лямбды в TypeScript

Много ли различий между следующими строками (особенно глазами разработчика на C#):

$.ajax({ … }).done(function (response: any) { … });
$.ajax({ … }).done((response: any) => { … });

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

$.ajax({ … })
    .done((response: any) => { … })
    .fail(function (response: any) { … })
    .always(function () { … });

Однако, с точки зрения TypeScript, разница между 2 и 3 строкой достаточно большая.

Лямбда воспринимается как член класса. При этом, внутри нее this указывает на экземпляр класса и его тип контролируется при компиляции. Если посмотреть JavaScript код, то можно увидеть замену this на _this.

Внутри функции указатель this получает тип any и указывает на вызывающий объект. Соответственно, контроль вызовов её функций и полей осуществляется только во время исполнения скрипта. В JavaScript коде this остается без изменений.

Таким образом использование лямбды более предпочтительно из-за типизации this и контроля типов и вызовов уже на этапе компиляции.

Поставляем правильный this в функцию

Иногда поведение this в функции можно компенсировать явно указав контекст, где это возможно:

$.ajax({ …, context: this })
    .done(function (response: any) { … })
    .fail(function (response: any) { … })
    .always(function () { … });

Сложности замены функции на лямбду

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

Например, в AngularJS есть переменная $scope, которая содержит $parent - указатель на своего родителя. Их базовые типы объявлены в TypeScript. Но $parent может содержать дополнительные переменные.

В случае использования функции возможен вот такой вызов: this.$scope.$parent.someValue. Поскольку this имеет тип any, то $scope, $parent и someValue также будут any. Ошибки компиляции не будет и сам код будет работать, если parent действительно содержит такую переменную.

Разумеется, при замене на лямбду будет ошибка из-за неизвестной someValue.

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

Intellisense

В завершении стоит отметить интересный факт. Внутри функций Visual Studio Intellisense предлагает достаточно корректный список постановок для this. Если не присматриваться, то можно даже не заметить разницы с лямбдой.

Добавить комментарий