/TypeScript-materials

TypeScript learning materials

Primary LanguageTypeScript

TypeScript-materials

Встановлення та налаштування

$ npm install -g typescript

І тепер одразу додамо маленький сервер

npm i --save-dev lite-server

Перейдемо в package.json і додамо в блок scripts нове значення "start": "lite-server", у вас повинно вийти якось так:

-----------------------------------------------------------------------------------------------------------

Пісял створення кожного файлу app.ts

Скомпілюйте файл наступною командою

$ tsc app.ts

Виконаємо ініціалізацію.

$ npm init -y

Ця команда стежить за зміною файлу та компілює його при кожній зміні

$ tsc app.ts -w

tsc app.ts -watch

Якщо потрібно компілювати одразу декілька файлів то використовують команду

$ tsc --init

У нас в проекті з'явиться файл tsconfig.json з купою закоментованих налаштувань.

Якщо тепер ми просто виконаємо команду. То скомпілюються всі файли з розширенням tc.

$ tsc

$ tsc -w

------------------------------------------------------------------------------------------------------

Опис простих

Number

String

boolean

null

undefined

Array - масив рядків. let arrString: string[];

Object - Якщо тип object const obj: object = {};

Типи, яких немає в js

Типи для змінних та аргументів

Any - Вказуючи цей тип, ми робимо змінну як у js, туди можна передати все, що завгодно.

Unknown - а змістом це те саме, що й any, тільки коли ми будемо намагатися його зберегти в іншу змінну, ми отримаємо помилку.

Tuple - Одним словом - це незмінний масив.

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

image

Enum - Це навіть патерн, але він такий популярний, що в typeScript вирішили додати його як тип даних.

За правилами гарного тону змінна, яка в enum, починається з великої літери.

image

Union Type - Це коли ми говоримо, що змінна або аргумент може містити кілька типів. Типи перелічуються через |

Literal Type - Чимось схоже на Union Type, але тільки замість типів ми передаємо рядки.

image

Типи для методів та функцій

Return Typ - Ми можемо вказувати тип, який повернеться з функції.

image

Void - Це коли функція нічого не повертає.

image

Never - Це коли функція ніколи не закінчується та нічого не повертає. Наприклад, listen в express, оскільки підключення до сервера постійне, або якщо ми повертаємо throw, оскільки це помилка, можна сказати, що функція ніколи не закінчується.

image

Function Type - Ми можемо описати функцію як тип, це особливо актуально для callback або, коли ми просто прокидаємо функцію.

image

Custom Types

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

Тип створюється за допомогою команди type, ім'я типу задається з великої літери.

Ми також можемо експортувати тип даних із файлу.

image

image

Опціональні параметри та властивості

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

image

image


Інкапсуляція

Майже у всіх мовах, які підтримують ООП, є три модифікатори доступу.

public - це як всі властивості та методи в js, можна викликати будь-де private - не можна викликати ззовні екземпляра, не наслідується protected - не можна викликати ззовні екземпляра, але наслідується

image

Наслідування

image

Поліморфізм

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

image

Абстракція

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

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

image

Readonly

Цей оператор дозволяє зробити властивість як const, можна тільки читати, але не можна модифікувати.

image

Getter/Setter

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

Статичні методи та властивості

Щоб створити властивість або метод статичним, просто після модифікатора вказуємо static.

image


Принципи ООП (S.O.L.I.D)

  1. SPR (Single responsibility principle) - Принцип єдиного обов'язку Принцип означає, що кожен об'єкт повинен мати один обов'язок і цей обов'язок повинен бути повністю інкапсульований у клас. Якщо клас працює з базою даних, він працює тільки з базою даних, якщо клас працює із зображеннями, він не працює з текстом і так далі.

  2. OCP (Open/closed principle) - Принцип відкритості/закритості Класи, методи, функції мають бути відкриті для розширення, але закриті для зміни.

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

  1. LSP (Liskov substitution principle) - Принцип підстановки Барбари Лісков Методи, які використовують певний тип, повинні мати можливість використовувати його підтипи, не знаючи про це.

  2. ISP (Interface segregation principle) - Принцип поділу інтерфейсу Клієнти не повинні залежати від методів, які вони не використовують.

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

  1. DIP (Dependency inversion principle) - Принцип інверсії залежностей Формулювання:

Модулі верхніх рівнів не повинні залежати від модулів нижніх рівнів. Обидва типи модулів повинні залежати від абстракцій. Абстракції не повинні залежати від деталей. Деталі повинні залежати від абстракцій. Мається на увазі, щоб не створювати об'єкт всередині.

Висновок ✌ TypeScript підтримує всі принципи ООП. Але не потрібно гнатися за всіма цими принципами, тупо відповідати їм - також погана практика, як і не відповідати їм взагалі. Якщо вас запитають на співбесіді, гівнокод або якісний код з усіма принципами ООП, скажіть сміливо, дивлячись скільки часу на завдання, якщо потрібно швидко, краще робочий гівнокод.


Інтерфейси

Інтерфейс - це визначення кастомного типу даних, але без реалізації. У всіх мовах, які підтримують ООП, інтерфейси працюють однаково, чимось схоже на абстрактні класи, тільки в абстрактних є часткова реалізація і тільки деякі частини реалізації ми кладемо на плечі дочірніх класів, а ось в інтерфейсі взагалі відсутня реалізація, він просто описує, яка повинна бути структура. Нам необов'язково призначати інтерфейс класу, ми можемо вказати його як тип даних для об'єкта.

image

Extending Interfaces

Ми можемо розширювати наші інтерфейси іншими за допомогою команди extends.

image

UML та шаблони проектування

image

image

Шаблони проектування

Ми розберемо декілька шаблонів проектування. image

Цей шаблон дозволяє не плодити об'єкти, повертаючи той самий екземпляр.

image

image

Використовується, коли нам потрібно багато однотипних об'єктів із загальним інтерфейсом.

image

Generics

Ще говорять узагальнений тип, оскільки generic - це загальний, ну ви навряд чи в компанії почуєте напиши тут узагальнений тип, я далі буду просто писати дженерик. Ми знаємо, що масив - це об'єкт Array і хочемо уточнити, які типи він може містити.

let arr: Array<string | number> = [];

Давайте ще приклад, коли це просто необхідно, це коли ми працюємо з асинхронним кодом. Оскільки Promise може повернути все, що завгодно, без дженерика ми ніколи не будемо знати, що він повертає.

const promise: Promise = new Promise((resolve) => { setInterval(() => { resolve('Done!'); }, 1000); });

promise.then((data) => { console.log(data); });

Extends

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

function merge(objA: T, objB: U) {

return Object.assign(objA, objB);

}

const merged = merge({name: 'Alisa'}, { age: 20 });

merged.name;

Але давайте ще один приклад, у нас є функція, яка повертатиме довжину або рядка, або масиву. І ми вирішили використати дженерики для цього. І ми отримуємо помилку, тому що TS не знає, а чи може бути функція length у типі, давайте створимо інтерфейс і явно покажемо, що це може бути будь-який тип, аби був length.

interface ILength {

length: number;

}

function getLength(str:T) {

return str.length;

}

getLength('text');

Keyof

У нас на практиці напевно виникне потреба повернути значення з об'єкта, але, навіть якщо ви напишите дженерики, ви отримаєте помилку. Оскільки TS не може гарантувати, що цей ключ є в об'єкті. Для цього є спеціальний оператор keyof. Він дозволяє зробити уточнення, що якийсь тип є ключем в об'єкті. За допомогою U extends keyof T ми зробили уточнення, що тип U - це як ключ у типі T і тепер все працює.

function extractValue<T extends object, U extends keyof T> (obj:T, key:U) {

return obj[key];

}

extractValue({name: 'Sergei'}, 'name');

Utility Types

Ми розберемо три типи, які принаймні мені доводиться часто використовувати.

Partial - Іноді буває така ситуація, коли у вас є якийсь тип даних, і там всі поля обов'язкові, але ви не можете заповнити їх відразу, але впевнені, що в результаті заповните. Для цього можна скористатися типом Partial, як я казав, це все дженерики. Ми сказали, що person є типом Partial, і тепер всі поля стали опціональними. Але при поверненні з функції нам потрібно вказати тип, інакше буде помилка.

interface IPerson {

name: string; age: number; }

function createPerson (name: string): IPerson {

const person: Partial = {};

person.name = name;

person.age = 21;

return person as IPerson;

}

Readonly - Дуже зручна річ, пам'ятаєте ми робили тип кортежу? Але push все одно працювало, тож використовуючи цей тип, можна реально зробити незмінний масив.

const arr: Readonly<string[]> = ['One', 'Two', 'Three'];

або

type Environment = {

temperature: number;

}

const arr: Readonly = {

temperature: 27,

};

Pick - Теж зручний тип даних, якщо нам потрібно якось урізати тип, взяти частину полів із нього. Ми, наприклад, робимо сторінку анотацій і нам із типу Page потрібно лише кілька полів.

interface Page {

title: string;

annotation: string;

numberPage: number;

}

const pageAnnotation: Pick<Page, 'annotation' | 'numberPage'> = {

annotation: 'Small page',

numberPage: 1,

};