Так як ми уже розібрали основи та синтаксис в TypeScript, я відразу вам буду показувати приклади коду.
Масиви в TS:
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ['a', 'b', 'c'];
numbers.push(4);
strings.forEach((str) => console.log(str.toUpperCase()));
// на виході отримаєте
"use strict";
let numbers = [1, 2, 3];
let strings = ['a', 'b', 'c'];
numbers.push(4);
strings.forEach((str) => console.log(str.toUpperCase()));
Як бачите з прикладу у вас є 2 типи запису. Перший це коли ви вказуєте тип а після знак масиву - number[]
, а інший коли ви вказуєте Array
, та його тип <string>
.
У випадку, коли вам потрібно вказати відразу декілька типів, ви можете використовувати суміжні типи:
let mixed: (string | number)[] = ['hello', 42];
Колекції: Map та Set
Якщо ви вперше стикаєтеся з map set, то це нормально. Тому що вони хоча і були додані в JS дуже давно, але широкого використання або популярності не отримали. По факту Map це обьект, в якому ви можете задавати будь які ключі, навіть обʼєктом, та задавати значення. Це може бути користно при динаміці, та реактивності. Просто подивіться приклад (на нативному JS):
const map = new Map();
map.set("name", "Ivan"); // ключ — string
map.set(42, "The answer"); // ключ — number
map.set({ x: 10 }, "object"); // ключ — об'єкт
console.log(map.get("name")); // Ivan
console.log(map.get(42)); // The answer
В мапах можна використовувати for of, а також зберігається порядок вставки, що може бути користним. І тепер давайте перейдем до TS, щоб зрозуміти як це використовується в TS.
Map<K, V> — асоціативний масив
const userRoles: Map<string, string> = new Map();
userRoles.set("Alice", "Admin");
userRoles.set("Bob", "User");
console.log(userRoles.get("Alice")); // "Admin"
З прикладу видно, що ви задаєте типи для ключа та значення.
Ітерація по Map
for (const [user, role] of userRoles) {
console.log(`${user} => ${role}`);
}
Set<T> — множина (унікальні значення)
const uniqueNumbers: Set<number> = new Set([1, 2, 3, 3, 4]);
uniqueNumbers.add(5);
console.log(uniqueNumbers.has(3)); // true
WeakMap та WeakSet
Вгорі я пояснював що так map та set, тут в принципі теж саме, але у WeakMap та WeakSet є декілька відмінностей. WeakMap і WeakSet працюють тільки з об’єктами. Це зроблено для того, щоб не заважати збору сміття (garbage collection). Наприклад:
const secretData = new WeakMap();
const user = { name: "Alice" };
secretData.set(user, "Секретний токен");
console.log(secretData.get(user)); // "Секретний токен"
Якщо user стане недоступним (наприклад, його більше не використовують у коді), дані в WeakMap також будуть автоматично видалені. Також weakmap не перебираються (for..of, .keys(), .entries() — недоступні), та не мають .size — працюють тільки з об’єктами (не примітивами).
Ви можете задати логічне питання, а навіщо вони взагалі потрібні? Вони використовуються для кешування, як показано в прикладі вгорі з секретним токеном. Або для того, щоб не зберігати мертві об'экти, гарбажі )
Ось простий приклад використання для кешування, так вам буде більше зрозуміло:
const cache = new WeakMap<object, number>();
function heavyCalculation(obj: object): number {
if (cache.has(obj)) {
return cache.get(obj)!;
}
const result = Math.random(); // умовно важка операція
cache.set(obj, result);
return result;
}
const myObj = {};
console.log(heavyCalculation(myObj)); // обчислюється і кешується
console.log(heavyCalculation(myObj)); // береться з кешу
Так тепер перейдомо саме до TS прикладів:
WeakMap<object, value>
const privateData = new WeakMap<object, string>();
const obj = {};
privateData.set(obj, "секрет");
console.log(privateData.get(obj)); // "секрет"
WeakSet<object>
const visited = new WeakSet<object>();
const page = {};
visited.add(page);
console.log(visited.has(page)); // true
Генератори
Генератори — це функції, які можуть призупинити своє виконання та відновити його пізніше.
function* idGenerator() {
let id = 0;
while (true) {
yield id++;
}
}
const gen = idGenerator();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
Ітератори
Ітератор — це об’єкт з методом next(), який повертає { value, done }.
const iterableObject = {
data: [10, 20, 30],
[Symbol.iterator]() {
let i = 0;
return {
next: () => {
if (i < this.data.length) {
return { value: this.data[i++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (const item of iterableObject) {
console.log(item);
}
Комбінуємо генератори з колекціями
function* collectionPrinter<T>(items: Iterable<T>) {
for (const item of items) {
yield `Item: ${item}`;
}
}
const set = new Set(["apple", "banana"]);
for (const item of collectionPrinter(set)) {
console.log(item);
}
І на цьому мабуть урок все, дякую вам за увагу ) 🎉