Статьи

TypeScript як майбутнє ентерпрайзного JavaScript. Частина 1

  1. Битва з вітряними млинами
  2. ES6
  3. Бардак і порядок
  4. TypeScript - надбудова навколо ES6, ES2015
  5. Попередження і помилки компіляції
  6. Статична типізація і виведення типів
  7. Необов'язкові аргументи для функцій і значення за замовчуванням
  8. Public, private, protect для властивостей і методів класів
  9. Геттери і сеттери для властивостей «без головного болю»
  10. Декоратори для всього (ES7)
  11. Анотації - як це було (або могло бути)
  12. декоратори
  13. Інтерфейси і абстрактні класи
  14. Generics
  15. Компілювання в ES5 або навіть в ES3 (із застереженнями)
  16. ES6 в TypeScript
  17. Висновки

Не стану переказувати тут історію появи JS, вона прекрасно всім відома, і описується одним словом - поспіх Не стану переказувати тут історію появи JS, вона прекрасно всім відома, і описується одним словом - поспіх. JavaScript замислювався і створювався в дуже стислі терміни. За словами творця JS Brendan Eich, у них було лише 10 днів на все. Microsoft наступав на п'яти і, якби вони програли, то зараз ця стаття була б частково присвячена VBScript (Visual Basic Script).

Стаття розділена на дві частини. Перша частина описує мову TypeScript - я спробую роз'яснити, яким чином безліч нових концепцій TS проектуються на JavaScript. Друга частина буде присвячена процесу розробки і міграції існуючого коду на TypeScript, а також планам розвитку мови.

Битва з вітряними млинами

Браузерні світ вибрав JavaScript з усіма його недоліками, застереженнями і поверхневої простотою. З тих пір спливло багато води, сама мова змінився, обріс доповненнями і зручностями, але основні проблеми залишилися, і позбутися від них майже неможливо, не зламавши весь інтернет.

Перші веб-сайти були дуже прості і статичні. JavaScript використовувався лише в рідкісних випадках для анімації або валідації форм перед відправкою. Проблема полягала в тому, що далеко не всі браузери підтримували JavaScript. Коли ж він став стандартом для всіх популярних браузерів, почалася розробка додатків, які робили в рамках веб-сторінки щось більше, ніж анімація біжучого рядка або перевірка введених значень на формі зворотного зв'язку. Додатки ставали більше, і почали даватися взнаки проблеми JavaScript, пов'язані з типами даних, з відсутністю єдиного способу успадкування об'єктів, з моделлю пам'яті JS, а також багато іншого.

Сама мова сприяє написанню поганого коду. І прощає, принаймні спочатку, написання явною локшини замість коду. Динамічна природа мови просто призводить до написання універсальних функцій, які можуть приймати десятки варіантів аргументів (як за типом даних, так і за їх кількістю).

Ризикую бути закидані помідорами, так як зазіхаю на святе, але найпопулярніший приклад - jQuery . Варто хоча б згадати можливі варіанти головної функції-об'єкта jQuery. «Функція-об'єкт» - відчуваєте, як це звучить? Ви можете викликати jQuery як функцію дев'ятьма (дев'ятьма, Карл!) Різними способами - все залежить від аргументів. А ще, сама функція є об'єктом, в якому може бути неконтрольоване число методів і / або властивостей.

Так, jQuery - це дійсно швейцарський ніж з величезною кількістю можливостей щодо спрощення життя рядовому веб-розробнику. Але часто на базі системи jQuery-плагінів створюють цілі додатки інтернет-магазину з десятками форм і діалогів. І ось там вже починається натуральний пекло і «локшина-код». Це найпопулярніший приклад. jQuery розроблявся як засіб зручного доступу до DOM, але в підсумку вийшов комбайн і практично окрема мова програмування, що і породжує цілу окрему всесвіт божевілля, де на кожне питання одна відповідь - плагін для jQuery.

Так, написана величезна кількість фреймворків рівня додатки, які допомагають утримати всю міць динамічною локшини в вузьких рамках запропонованої / рекомендованої / необхідної структури файлів і папок. Тільки завдяки таким фреймворками ми з вами ще не зійшли з розуму остаточно.

У корпораціях кшталт Google технічні фахівці дуже швидко зіткнулися з тим, що з ростом розмірів JavaScript додатки практично з геометричною або навіть експоненційної швидкістю ростуть витрати на підтримку і виправлення помилок. У відповідь на цю проблему Google випустив GWT - Google Web Toolkit. Це компілятор, набір інструментів і базових класів для розробки веб-додатків на Java. Стало можливим з мінімальними застереженнями писати веб-додатки на строго типизированном мовою програмування з використанням більшості його булочок. Як результат ви отримуєте додаток, написане на, фактично, машинному JavaScript. Цей код неможливо налагоджувати окремо від GWT, це просто втрачає сенс. У грудні минулого року, після більш ніж року мовчання, проект випустив бету нової версії (2.8.0).

Варто також зауважити, що GWT частіше розглядають як єдину можливість для джавістов, які не володіють JS, писати розлогий front-end без відриву від улюбленого мови для back-end'а.

Інших же не влаштовував тільки синтаксис JavaScript, і вони розробляли свої варіанти мови програмування, які з різним ступенем прозорості транслюються в JavaScript. Тут список прикладів очолює, звичайно ж, CoffeScript . Підбірка всього, що так чи інакше транслюється в JavaScript, опублікована на github в wiki проекту CoffeScript.

Загальний сенс зводиться до того, що ви можете писати front-end додаток майже на будь-якій мові програмування, який вам подобається, важливо лише, щоб була можливість згенерувати JavaScript код на базі вашого вихідного коду.

ES6

ES6 приходить до нас як набір доповнень до вже звичного стандарту ES5. Набір цих доповнень неточний, постійно доповнюється і переробляється в тій чи іншій мірі.

Звичайно, з огляду на те, що браузерів більше, ніж один, і інтерпретаторів JS, як мінімум, стільки ж (навіть незважаючи на спільне коріння і / або основу у багатьох з них), кожен з них гне свою лінію і експериментує з синтаксисом, з трактуванням пропозицій спільноти, а також пропонує свої ідеї.

Це свого роду продовження війни браузерів, але вже в більш млявою формі. Ось тільки вона як і раніше приводить до умов перевірки браузера або навіть його версії в нашому коді, всіляких «поліфілії» (polifill), які ми змушені підключати до наших проектів, якщо хочемо використовувати якусь «смакота» з ES6, наприклад, Promise або setImmediate. Але це історія про браузерні API, а не про саму мову JavaScript.

Якщо ж ми хочемо використовувати трохи більше класичні (в порівнянні з іншими мовами) класи, генератори, не хочемо думати про контекст для колбек, то тут вже починаються проблеми: одна справа - бажання сучасного розробника, який хоче писати сучасний код, і, зовсім інша справа - реальність брузеров клієнтів. Ось тут на допомогу і приходять транспайлери і компілятори.

проект Babel вдає із себе транспайлер з сучасного уявлення про правильне перекладі ES6 або навіть ES2015 в код, сумісний з ES5. Тобто розробник отримує можливість писати на найсучаснішій версії JavaScript і, в більшості випадків, не турбуватися про сумісність з браузерами, які ще не включають підтримку ES6 в свої JS-движки (або не включають за замовчуванням, так як все, що приховано за спеціальними експериментальними настройками, краще вважати виключеним і недоступним). Про проект і всі його можливості ви можете прочитати на офіційному сайті, а ми підемо далі.

Бардак і порядок

Добре, ми можемо прозоро використовувати всі смаколики сучасного JavaScript у своєму коді і не турбуватися про сумісність з браузером.

Коли команда розробки складається з більш ніж однієї людини, то виникає необхідність в угодах - з архітектури, вибору зовнішніх бібліотек, фреймворка або списку фреймворків (про всяк випадок?). Більшість з цих питань вирішується усно за пару мітингів і уточнюється усно (звичайно ж документується, вірно?) На наступних мітингах.

З початком розробки з'являються нові питання і проблеми - ієрархії класів потрібно підтримувати здоровими і чіткими. Мінімалізм інтерфейсів великих компонентів потрібно строго документувати і постійно влаштовувати уважне рев'ю коду, щоб не пропустити момент, коли все піде криво і навскіс. А це обов'язково станеться. В якійсь команді / проект раніше, в якийсь пізніше, але це трапиться. І це буде схоже на притчу про жабу в каструлі з холодною водою на повільному вогні. У чому може бути причина такого розвитку подій?

Тут я висловлю виключно свою думку, яку ви не повинні сприймати як чисту правду або аксіому. Але справа ось у чому: динамічна природа JS підштовхує до зайвої гнучкості, і це расхлябивает інтерфейси об'єктів і функцій. Згодом ваші функції і конструктори об'єктів починають приймати все більше різновидів аргументів, об'єктів різного ступеня цілісності і така гнучкість в результаті призводить до зростання числа фантомних багів, які вирішуються підпорами то тут то там, що в свою чергу, ще сильніше підвищує ентропію в вашому коді .

Єдиний порятунок від такого сценарію - постійне жорстоке рев'ю кожного коммітов / пул-реквеста, де кожне розширення інтерфейсу ставиться під сумнів і відкидається, якщо завдання можливо вирішити вже існуючими засобами. Найчастіше такий випадок означає, що розробник хоче передати дані в незвичайному форматі, дані просто не підготовлені і розробник хоче перекласти відповідальність за перетворення даних на клас одержувача, а це вже архітектурне рішення. Ну і постійний рефакторинг і боротьба з технічним боргом. Так, це суперечить більшості ідей, що «Спочатку доставити клієнту продукт, а потім наведемо порядок» і, улюблене багатьма, «Ну, адже працює ж?». Це все зрозуміло, але крайності, - це завжди погано, потрібно шукати баланс, коли ви зможете і продукт доставити вчасно, і не перетворите процес розробки і підтримки в пекло для себе або інших ваших товаришів. Ну або хоча б відстрочите колапс цього локшина-коду на максимальний час.

Згідно з другим законом термодинаміки, ентропія завжди зростає, але в наших силах хоча б уповільнити цей процес.

TypeScript - надбудова навколо ES6, ES2015

Прийшов час перейти до головної теми. TypeScript - це мова програмування, який є супер-сетом навколо ES6, ES2015. Розроблено в Microsoft і тепер розвивається в співдружності з ком'юніті. Навіть Google доклав руку у вигляді AtScript, був поглинений однією з попередніх версій TypeScript. Запитайте, якщо хочете, подробиці у Google.

Що з себе являє супер-сет? Це надбудова навколо основного мови. Таким чином, кожен працюючий JavaScript-код автоматично є дійсним TypeScript-кодом.

Таким чином, кожен працюючий JavaScript-код автоматично є дійсним TypeScript-кодом

Що нового привносить TypeScript:
- Статична типізація і виведення типів;
- Необов'язкові аргументи для функцій і значення за замовчуванням;
- public, private, protect для властивостей і методів класів;
- Геттера і сеттери для властивостей «без головного болю»;
- Декоратори для всього *;
- Інтерфейси і абстрактні класи;
- Generics;
- Компілювання в ES5 або навіть в ES3 (із застереженнями).

І в тому числі плюшки ES6:
- Arrow Functions;
- Класи з єдиним стилем спадкування;
- async / await з ES7;
- Ітератори і Генератори *;
- Багаторядкові рядки з шаблонізаціі і теж «без головного болю» з плюсами і лапками;
- Управління залежностями, в тому числі і їх динамічне завантаження.

* - (мета компіляції - не нижче ES5) експериментальна підтримка, імплементація може змінитися.

Головне, що потрібно засвоїти при початку використання TypeScript - ніякої магії компілятор не робить, і що бронежилет він на вас теж не одягає. Ви як і раніше можете писати низькоякісну локшину, і компілятор це переварить, хоч і завалить вас попередженнями.

Переважна більшість роботи по захисту вас від вас же компілятор виробляє саме в момент аналізу типів даних і їх взаємодії. Взагалі вся історія про TS так чи інакше зводиться до звірки типів даних і інтерфейсів об'єктів. Тільки після цього починають працювати перетворювачі синтаксичного цукру. Вони чимось нагадують прості макроси, які за шаблоном переробляють рядки (звичайно, це грубе порівняння, але воно близьке до реальності).

Компілятор не буде проти складання числа з рядком або навіть з об'єктом, він лише видасть попередження про те, що ви, оголошуючи тип, мали на увазі, швидше за все, щось інше, і вам варто звернути увагу на конкретно цей рядок і перевірити, все чи ви тут робите усвідомлено.

Попередження - головна зброя компілятора. Через попередження, які він видає в консолі, він повідомляє вам про свої «сумнівах» щодо якості коду - «Ви вказали, що ця змінна має тип Рядок, але ось тут ви використовуєте її як Число, ви впевнені?» (Це дуже вільний переказ повідомлення компілятора). Якщо ви впевнені, і ваш вибір конкретно такої логіки усвідомлений, то ви можете таку малу змінну доповнити приведенням типу до числа (або скористатися, наприклад, parseInt ()), явно повідомляючи компілятору, що ситуація під контролем.

Весь генерується JS-код в результаті роботи компілятора може бути легко читаним нормальним JS-кодом, яка зберегла імена змінних, об'єктів і властивостей, ваші коментарі в потрібних місцях - все те, що дасть вам можливість легко зіставити TypeScript-код з JavaScript-кодом для кращого розуміння мови.

Це насправді зручно і практично, особливо на самому початку освоєння TS, його впровадження в існуючий проект - ви можете писати TypeScript-код і тут же бачити одержуваний JavaScript-код.

Попередження і помилки компіляції

У більшості випадків компілятор може скомпілювати код, навіть з попередженнями. Можливих помилок, що зупиняють компіляцію, досить мало - наприклад, деякі синтаксичні конструкції на кшталт декораторів неможливо скомпілювати в код, сумісний зі стандартом ES3. Але це не означає, що ви не можете налаштувати найжорстокіші правила для свого проекту.
Наприклад, пакет компіляції TypeScript для grunt-ts ви можете налаштувати так, що він буде обривати збірку навіть при одному попередженні від компілятора.

Таке толерантне ставлення до попереджень у компілятора пов'язано в першу чергу з тим, що дуже часто TypeScript-код працює разом зі звичайним JavaScript-кодом, і TypeScript-частина коду може не повною мірою описувати свій зв'язок з JS-частиною. Тому компілятор працює в умовах неповної картини світу і в попередженнях повідомляє якраз про таких гострих для нього краях цього світу.

Лише найближчим часом компілятор TypeScript навчиться використовувати в своїй роботі JavaScript-код (тобто код в файлах .js) - це дозволить розширити сферу його можливостей до аналізу на jsDoc-коментарі, де часто можна зустріти в тому числі типи даних для змінних, аргументів і безпосередньому виведенню типів на основі коду.

Будь JavaScript - це валідний TypeScript. Так як це супер-сет навколо JavaScript, це означає, що ви можете використовувати все, що є в JavaScript всіх версій, і те, що привносить безпосередньо TypeScript.

Статична типізація і виведення типів

У вас є базовий набір типів JavaScript з явними деклараціями, плюс парочка додаткових, на кшталт enum (безліч) і tuple (тьюпли, схожі за концепцією з такими в python).

Всі типи даних, явно декларовані для змінних і властивостей класів, звичайно ж, потрібні тільки для перевірок на етапі компіляції, ніякої перевірки типів на етапі виконання не вноситься. Все декларування типів видаляється.

Компілятор досить розумний, щоб не вимагати повсюдного вказівки типів, значну частину типів він здатний вивести, виходячи з вашого коду. Місцями це зручно і дозволяє писати лаконічний компактний код. Але іноді все ж слід себе змушувати і явно описувати типи, підвищуючи читаність коду для інших програмістів, та й для вас з «завтра».

наприклад:

function sum (a, b: number) {return a + b; }

В наведеному вище прикладі немає необхідності описувати повертається функцією результат. Компілятору буде зрозуміло, що складання двох чисел завжди передбачає в результаті число.

Але якщо волею програмної логіки ви змушені реалізувати функцію, чий результат по типу може бути дуже різним (рядок, логічне, об'єкт, вже не знаю, як вам взагалі така ідея прийшла в голову, але так добре), або розміри функції не дозволяють, окинувши її поглядом, зрозуміти, який тип даних вона поверне, то його краще вказати явно, навіть якщо компілятор вас про це не просить (він зміг вивести тип результату).

Також TypeScript, як говорилося раніше, залишає вам можливість використовувати всі багатства динамічного мови і дозволяє вводити особливий тип даних any. Цей тип даних говорить компілятору буквально наступне: «У цієї змінної може бути все що завгодно».

Необов'язкові аргументи для функцій і значення за замовчуванням

Без передмов, приклад, який розповість відразу все:

function F1 (a, b: number, c: {name: string}, d: boolean, ... otherParams: string []): void {..} function F2 (a, b ?: number, c ?: { name: string} | number | string): void {..} function F3 (a, b ?: number, C = 10): number {..}

Крім оголошення типу або структури (що по суті є типом, але без імені) аргументу, в F1 використовується конструкція для функції з необмеженою кількістю аргументів (при виклику). Фокус полягає в тому, що в змінну otherParams будуть поміщені всі інші (після четвертого) аргументи, з якими буде викликана функція. Звичайно, для цього буде згенеровано кілька рядків JS-коду, які люб'язно відокремлять ці аргументи з arguments в масив.

F2 описує випадок, коли в аргументах функції є необов'язкові елементи - <імяПеременной>? . Це означає, що для цих параметрів при виклику функції компілятор буде нагадувати в попередженнях про невідповідність заголовка функції її викликом. Мінлива c - мало того, що необов'язкова, так ще й описує кілька варіантів свого типу. Тобто ця змінна може бути універсальною за типом - компілятор простежить, щоб тільки ці типи даних використовувалися при виконанні функції з цим аргументом.

F3 показує приклад з аргументом, Який має значення за замовчуванню. Синтаксично тут все просто, з точки зору генерується JS-коду теж - створюється умова, що перевіряє аргумент на існування, якщо аргумент не визначений, йому присвоюють це значення.

Як бачите, все те, що раніше нам доводилося знову і знову повторювати в своєму JS коді, найпростіші конструкції, в які іноді все ж закрадалися прикрі помилки, тепер можна не писати. Синтаксичний цукор TS допомагає зробити код зрозуміліше і наочніше.

Ті з вас, хто знайомий з мовою Python, можуть запитати, чому автори не пішли далі і не додали передачу аргументів на ім'я, приблизно ось так:

let x = F3 (a = 1, c = 3);

Я, звичайно ж, висловлю лише свою думку, я навіть не знаю, чи обговорювалася така ідея серед розробників мови. Але у цієї ідеї є явна проблемна сторона - реалізувати такий синтаксис можливо через обертання всіх аргументів на об'єкт. Але тоді такі функції будуть втрачені для зовнішнього коду написаного на JS. Хоча, звичайно, можна уявити заглушку і на цей випадок, але тоді що генерується код стане істотно складніше, та й буде складніше зберегти прозорість трансляції та збільшиться ризик колізій в іменах аргументів і полів в переданих структурах. Що якщо зовнішній JS код викликає таку функцію і в першому аргументі передає об'єкт в якому є поля, чиї імена збігаються з іменами аргументів функції? Вводити заборону на «перший складний аргумент функції»? Це виглядає, як мінімум, дивно.

Public, private, protect для властивостей і методів класів

Про цей пункт можна було б розповісти в розділі «Без магії, або Контроль над ситуацією».

Звичайно ж, в об'єктній моделі JavaScript в прототипах об'єктів не існує поняття доступності поля з нащадка або споживача. І TypeScript Не додає його через найхитріші приховування змінних в областях видимості. Все простіше. Якщо конкретне поле або метод класу ви описали як private, але трохи пізніше намагаєтеся звернеться до нього ззовні, то компілятор скаже вам про це. Але знову ж таки, не дивлячись на попередження в консолі при компіляції, компілятор слухняно згенерує звернення до цього полю об'єкта в JS-коді.

Геттери і сеттери для властивостей «без головного болю»

Тут все просто - ви використовуєте один синтаксис get name () {}, а TypeScript генерує для вас спосіб визначення таких властивостей через стандартний Object.defineProperty. При цьому вам доступний і варіант з обчислюються іменами таких властивостей .

Декоратори для всього (ES7)

Це експериментальний функціонал, доступний при включенні опції -experimentalDecorators і при компіляції в JS версії не нижче ES5

Декоратори - це синтаксичний цукор і патерн одночасно.

У ранніх версіях TS пропонувалося використовувати анотації, але від цього відмовилися на користь декораторів.

Анотації - як це було (або могло бути)

Анотації - це позначки на суті. Виглядало це приблизно ось так (в старих версіях TS і AtScript, звідки це і прийшло):

function annotate (obj) {...} // TS @annotate class A {...}

А тепер JavaScript:

// JS function A () {}; A .__ annotations = [annotate (A)]; // або function A () {}; A.annotations = [annotate (A)];

Тобто до об'єкта прикріплялося додаткове властивість __annotations або annotations, які можна було використовувати на свій розсуд. Ви помітили це «__annotations або annotations»? В цьому і ховалася проблема, різні імплементації допускали різні варіанти, що вводило плутанину. Проблема ускладнювалася ще більше, якщо існував зовнішній код, який міг використовувати ці властивості, але не знав, який саме варіант потрібно шукати в об'єкті, якщо взагалі знав, що потрібно щось шукати, в результаті застосування такого згенерованого коду в модулі написаному на JS , обростала умовами перевірок виду аннотирования, що не сприяло якості коду.

декоратори

Декоратори в свою чергу «декорують» об'єкт, обертають собою сутність і, якщо потрібно, підміняють її.

Суть простіше продемонструвати на простому прикладі. Уявімо клас, який надає доступ до якоїсь адмінки і коротко виглядає ось так:

class AdminPage {isAuth: boolean = false; isRoot: boolean = false; @guest ({ifauth_redirect_to: 'home'}) public login () {..} @auth public fetch (order: string []) {..} @auth @rootUser public destroyUniverse () {..}}

Декоратори в цьому прикладі описують рівні доступу поточного користувача до конкретних методів класу:
- Метод login доступ до курсу, інакше перенаправити на сторінку home;
- Метод fetch доступний тільки авторизованому користувачеві;
- Можливість викликати кінець світу методом destroyUniverse дана тільки авторизованому користувачеві з правами рут-користувача.

Такий стиль класу і використання декораторів повинен викликати почуття дежавю як мінімум у пітоністов і джавістов. Вони звикли до такого підходу.

Довго не затримуючись, давайте подивимося на імплементацію декоратора guest:

function guest (options: {ifauth_redirect_to: string | boolean} = {ifauth_redirect_to: false}) {// place for any logic with options before return function generator return function (target: any, methodName: string, desc: PropertyDescriptor) {// 1 let origin = target [methodName]; target [methodName] = function () {if (this.isAuth && options.ifauth_redirect_to) {this.redirect (options.ifauth_redirect_to); } Else {origin.apply (this, arguments); }} Return target; } // 1}

Отже, функція декоратора повинна повернути функцію, яка буде викликана для об'єкта декорування при створенні об'єкта.

У разі декорування методу класу функція-результат роботи генератора декоратора отримає прототип об'єкта, рядкове ім'я методу і опис методу, якщо воно існує .

Як видно з коду, ми зберігаємо оригінальний метод і замінюємо його своїм, в якому перевіряємо потрібні поля екземпляра об'єкта і викликаємо оригінальну функцію, якщо потрібно.

Можливість відповісти на питання «Як застосовувати декоратори в реальній практиці і навіщо вони вам потрібні?» Я залишу вам. Ця концепція має дуже потужну основу і може істотно вплинути на архітектуру вашого застосування.

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

Інтерфейси і абстрактні класи

Ні інтерфейсів, ні абстрактних класів в JavaScript немає, як і немає розумних аналогів їх подання. Тому тут мова знову про синтаксичний цукор.

Наведу відразу об'ємний приклад з коментарями, де я постарався використати майже все:

interface C {abc: boolean; } Interface D {dd: number []; } Abstract class B {// компілятор "не дозволить" створити екземпляр цього класу constructor (public name: string) {// аргумент name в конструкторі описаний так, // щоб автоматично було створено властивість name // з публічним рівнем доступу} test ( a, b) {return a === b; // для цієї функції типи не потрібні} abstract length (a, b: string): number // клас нащадок зобов'язаний // імплементувати цей метод // або оголосити його знову абстрактним (як і сам клас)} class A extends B implements C , D {static x: number = 5 private abc = true public dd = [10, 11] constructor () {super ( "A"); // компілятор буде наполягати, щоб в першому рядку // конструктора викликали конструктор предка} length (a, b: string): number {// обіцяна імплементація return (a + b) .length; }}

Використання інтерфейсів в TypeScript відверто привносить порядок в божевільний світ динамічного програмування на JavaScript. Звичайно ж, при компіляції від інтерфейсів не залишається і сліду. Але поки ведеться розробка, вони допомагають контролювати ситуацію, уникаючи дублювання властивостей і методів, підтримуючи угоди між різними частинами системи, надаючи можливість IDE будувати адекватні припущення і підказки.

Ще приклад ( взятий з офіційної документації ):

interface IShape {color: string; } Interface ISquare extends IShape {// інтерфейси успадковуються простим об'єднанням sideLength: number; } // створення змінної з порожнім об'єктом, але із зазначенням його типу або типу структури let square = <ISquare>; {}; square.color = "blue"; square.sideLength = 10; // спроба додати нову властивість square.a = 1; // викличе попередження - цього властивості немає ні в одному // з інтерфейсів.

У цьому прикладі показаний випадок створення типу даних структури без використання класів. Вони не є обов'язковими для таких випадків. Вигода від такого підходу в тому, що ви явно просите компілятор допомогти вам не помилитися при роботі зі змінною. Звичайно, на базі цих же описів IDE зможе запропонувати вам гідний набір автодоповнення і навіть, можливо, рефакторинг.

А тепер, інтерфейс для функції ( взятий з офіційної документації ):

interface SearchFunc {(source: string, subString: string): boolean; } Let mySearch: SearchFunc; mySearch = function (source: string, subString: string) {let result = source.search (subString); return result! = -1; }

Навіщо це потрібно? Ну, наприклад, ви можете описати інтерфейс необхідного колбек. Якщо вам в функцію спробують передати функцію з іншим інтерфейсом, компілятор попередить про це.

У цьому розділі я навів лише малу частину можливостей і особливостей інтерфейсів, настійно рекомендую прочитати взятий з офіційної документації , Там ще багато корисного.

Generics

Непростий функціонал, хоч в базовому випадку і працює дуже просто. Приклад з документації:

class GenericNumber <T> {zeroValue: T; add: (x: T, y: T) => T; } Let myGenericNumber = new GenericNumber <number> (); myGenericNumber.zeroValue = 0; myGenericNumber.add = function (x, y) {return x + y; };

Як і у випадку з інтерфейсами, краще прочитати главу в документації для повного розуміння. Якщо дуже коротко: компілятор підміняє T в декларації класу на number і перезапускає аналіз, ніби у класу там всюди number. От і все. Знову ж таки, до JavaScript-коду вся ця магія (як же я намагався уникати цього слова) не доходить, все перевіряється / звіряється / виводиться до трансляції в JS-код.

Компілювання в ES5 або навіть в ES3 (із застереженнями)

Тут все просто, компілятор має всередині себе заготовки різних конструкцій, на зразок наслідування, у вигляді шаблонів або шматків синтаксичного дерева (це не важливо), для різних версій стандарту EcmaScript (зі списку підтримуваних).

Застереження стосуються експериментальних конструкцій на кшталт декораторів і того, що просто неможливо описати в конкретної версії EcmaScript. Наприклад, ні декоратори, ні set / get для полів класу можна описати в стандарті ES3 - просто в цьому стандарті немає потрібних викликів в API примітиву Object.

Вибір мети компіляції, гарантії, що будуть доступні в якості мети і майбутні версії EcmaScript, - це робить TypeScript мало не срібною кулею. Ви вже можете використовувати все те, що придумано нового в синтаксисі JavaScript, використовувати перевірку типів, декларації інтерфейсів, абстрактні класи, декоратори і т.д. І в момент, коли «бізнес вирішить», що браузери клієнтів уже готові для ES6 або ES7, ви просто перемкніть компілятор на іншу мету, і генерується код знайде нові конструкції, позбавиться від якихось підпірок для забезпечення сумісності. Код потенційно навіть може стати швидше, так як замість явних обхідних шляхів буде використовувати нативное API движка JS.

ES6 в TypeScript

Як було сказано на самому початку, TypeScript є супер-сетом навколо JavaScript. Це означає, що він включає в себе всі синтаксичні конструкції, які додані в JS в ES6. Я не бачу сенсу висвітлювати їх тут, це замітка все ж про TypeScript.

Висновки

Синтаксичний цукор і перевірки безлічі параметрів при компіляції - ось два інгредієнта, які суттєво покращують процес розробки і страхують розробника від несподіваних дзвінків посеред ночі.

Внесіть порядок і структуру в ваш код і ... (тут допишіть свій варіант рекламного гасла).

Звичайно, компілятор не може захистити вас від архітектурних помилок. Але може попередити про можливі невідповідності між задуманим (інтерфейсом) і реалізацією (класом, функцією, структурою). Типізація змінних і аргументів допоможе бути впевненим, що ви правильно прочитали інтерфейс і застосовуєте суті правильно, а не керуєтеся лише своїм внутрішнім чуттям. Декоратори можуть істотно вплинути на сам підхід до оформлення класів і архітектури в цілому. Шаблони класів (generics), абстрактні класи, допоможуть не допустити втрату контролю над архітектурою додатки. Ну і весь інший синтаксичний цукор полегшує життя і робить код більш виразним.

У другій частині розповім про тонкощі процесу розробки на TS і міграції вже існуючого коду, а також про плани розвитку TypeScript.

«Функція-об'єкт» - відчуваєте, як це звучить?
Про всяк випадок?
Звичайно ж документується, вірно?
У чому може бути причина такого розвитку подій?
Так, це суперечить більшості ідей, що «Спочатку доставити клієнту продукт, а потім наведемо порядок» і, улюблене багатьма, «Ну, адже працює ж?
Що з себе являє супер-сет?
Через попередження, які він видає в консолі, він повідомляє вам про свої «сумнівах» щодо якості коду - «Ви вказали, що ця змінна має тип Рядок, але ось тут ви використовуєте її як Число, ви впевнені?
A, b ?
Number, c ?
A, b ?

Новости