Превратите теорию урока 2 в рабочий код: массивы задач, типы, интерфейсы, async/await и обработка ошибок.
💡 Этот файл — тренировка. Мы строим маленький «TaskFlow»: список задач, как в телефоне. Каждый кусок кода разбираем по шагам, как на занятии у доски.
map, filter, find, reduceinterface и typeasync/await с обработкой ошибокДан массив задач. Выполните четыре операции и выведите результаты.
id), надпись (поле title), пометка «сделано/не сделано» (поле completed) и важность (поле priority). Методы вроде filter и map — это действия с полкой: отобрать, переписать, найти, посчитать.
Контекст. В TaskFlow весь список задач хранится именно как массив объектов. Здесь мы учимся брать этот массив и получать из него нужную информацию без ручного перебора.
const tasks = [
{ id: 1, title: "Изучить JS", completed: false, priority: "high" },
{ id: 2, title: "Изучить TS", completed: true, priority: "low" },
{ id: 3, title: "Создать Angular", completed: false, priority: "medium" }
];
// 1. Активные задачи (completed === false)
// filter — как ситечко для чая: пропускает только подходящие
const active = tasks.filter(t => !t.completed);
// 2. Только заголовки
// map — как конвейер: берёт каждую задачу и возвращает только её название
const titles = tasks.map(t => t.title);
// 3. Найти задачу с id === 2
// find — как поиск коробки по номеру на полке
const found = tasks.find(t => t.id === 2);
// 4. Сколько выполнено
// reduce — как накопительный итог: идём по всем и суммируем результат
const doneCount = tasks.reduce(
(count, t) => count + (t.completed ? 1 : 0), 0
);
console.log({ active, titles, found, doneCount });
filter — ситечко для чая: проливает только те задачи, где completed равно false.map — конвейер/ксерокс: на входе задача, на выходе только её название.find — поиск по номеру: берёт первую коробку с нужным id и останавливается.reduce — накопительный итог: бежит по списку и складывает единицы там, где задача выполнена.Пошаговый разбор:
tasks из трёх объектов-задач.filter проходит по каждой задаче и оставляет только те, где !t.completed (не выполнено). Останутся задачи 1 и 3.map берёт каждую задачу и возвращает лишь t.title — получаем массив строк.find возвращает первую задачу с id === 2 (это «Изучить TS»).reduce начинает с 0 и прибавляет 1 за каждую выполненную задачу. Выполнена только задача 2 — итог 1.Что увидит студент в консоли:
{
active: [ {id:1,...}, {id:3,...} ], // две невыполненные
titles: ["Изучить JS", "Изучить TS", "Создать Angular"],
found: { id: 2, title: "Изучить TS", completed: true, priority: "low" },
doneCount: 1
}
active.length должен быть 2, doneCount — 1, а found.title — «Изучить TS».Опишите модель Task и функцию, которая возвращает только активные задачи. Укажите типы явно.
Контекст. В большом приложении (Angular/React) у задачи должна быть чёткая форма. Интерфейс Task — это «контракт»: какие поля обязательны и какого они типа. Функция getActive берёт массив таких задач и отдаёт только невыполненные.
// Бланк анкеты задачи: какие поля бывают и какого они типа
interface Task {
id: number; // номер — только число
title: string; // название — только текст
completed: boolean; // сделано или нет — true/false
priority: "low" | "medium" | "high"; // важность — один из трёх вариантов
}
// Функция получает массив Task и возвращает тоже массив Task
function getActive(tasks: Task[]): Task[] {
return tasks.filter(t => !t.completed); // оставляем невыполненные
}
// Пример вызова
const result = getActive([
{ id: 1, title: "A", completed: false, priority: "high" },
{ id: 2, title: "B", completed: true, priority: "low" }
]);
Пошаговый разбор:
Task — описание «бланка».getActive, помечая вход (tasks: Task[]) и выход (: Task[]) типами.filter, чтобы отсеять выполненные.result попадёт только задача с id: 1.Что увидит студент: переменная result содержит один объект { id: 1, title: "A", completed: false, priority: "high" }. А если написать priority: "urgent" — TypeScript подчеркнёт ошибку ещё до запуска.
Task станет контрактом между сервисом, компонентом и шаблоном. Ошибку в имени поля (например, titel вместо title) TypeScript покажет до запуска — и вы исправите её за секунду, а не ловили бы в рабочем приложении.Напишите универсальную функцию first<T>, которая возвращает первый элемент массива.
T (от слова Type) — это «пока не знаю что, но один вид». Когда вы кладёте строки — коробка становится «коробкой строк», кладёте задачи — «коробкой задач». Функция first просто достаёт первый предмет из любой такой коробки.
Контекст. В TaskFlow нам часто нужна одна и та же логика для разных типов данных. Generics позволяют написать функцию один раз и использовать её и для названий задач (строк), и для самих задач (объектов), не теряя проверку типов.
// T — «любой тип», который подставится при вызове
function first<T>(items: T[]): T | undefined {
return items[0]; // вернёт первый элемент или undefined, если пусто
}
const title = first(["Изучить JS", "Изучить TS"]); // string | undefined
const task = first([{ id: 1, title: "A", completed: false, priority: "high" }]);
Пошаговый разбор:
first<T>: на вход массив элементов типа T, на выход — один такой элемент или undefined.T это string, значит title имеет тип string | undefined.T это наш объект задачи, и task имеет соответствующий тип.Что увидит студент: в title попадёт "Изучить JS", а в task — объект { id: 1, title: "A", completed: false, priority: "high" }. Редактор подскажет поля объекта, потому что тип известен.
task? (Подсказка: это объект с полями id, title, completed, priority.)Сделайте запрос к mock-серверу и корректно обработайте HTTP-ошибку и сетевой сбой.
await — вы стоите у двери и ждёте курьера, не делая ничего другого в этой строчке кода. Когда посылка пришла — вы её распаковываете (response.json()). А try/catch — это страховка: если магазин закрылся или посылка потерялась, вы не падаете в обморок, а спокойно записываете «не получилось» и продолжаете жить (возвращаете пустой список).
Контекст. В реальном TaskFlow задачи обычно приходят с сервера, а не лежат в коде. Здесь мы учимся запрашивать их и быть готовыми к двум бедам: сервер ответил «ошибка» (404/500) и сервер вообще не отвечает (нет сети).
// async — функция умеет «ждать»; Promise — это обещание прислать результат
async function loadTasks(): Promise<unknown> {
try {
// ждём ответа от сервера, как посылку от курьера
const response = await fetch("http://localhost:3000/tasks");
// сервер может ответить "не найдено" (404) или "ошибка" (500)
// fetch сам НЕ бросает ошибку на такие ответы — проверяем вручную
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
// распаковываем посылку: переводим JSON в данные
return await response.json();
} catch (error) {
// Сюда попадём и при 404/500, и при выключенном сервере
console.error("Не удалось загрузить:", error);
return []; // безопасный итог: пустой список, приложение не падает
}
}
Пошаговый разбор:
async, чтобы внутри работал await.try делаем fetch и ждём ответа через await.response.ok: если статус неуспешный — сами бросаем ошибку через throw.response.json()).catch, мы логируем ошибку и возвращаем пустой массив, чтобы код дальше работал.Что увидит студент: при работающем сервере — загруженные задачи; при выключенном сервере — в консоли сообщение «Не удалось загрузить: …» и пустой список вместо аварийного завершения.
if (!response.ok) fetch НЕ бросит ошибку при статусе 404/500 — код посчитает их успехом и попытается распаковать «мусор» как JSON.Отредактируйте код и нажмите Запустить (или Ctrl+Enter):
Контекст. Здесь живой TaskFlow на чистом JavaScript. Слева — код, справа — что он рисует на странице. Функции getActiveTasks и toggleTask повторяют то, что мы разбирали выше, но прямо в браузере.
toggleTask. Меняй код и запускай снова.completed у задачи 1 на true и запустите — галочка в списке изменится. Или вызовите toggleTask(tasks, 2) и посмотрите, как меняется вторая задача.map и filter? (Подсказка: map преобразует каждый элемент и сохраняет их количество, а filter только отбирает — и может стать меньше.)find может вернуть undefined и как это учесть? (Подсказка: если задачи с таким id нет, результат будет пустым — проверяйте перед использованием.)fetch не бросает ошибку при 404? (Подсказка: для браузера ответ «страница не найдена» — это тоже успешный ответ, поэтому статус проверяют вручную через response.ok.)Практика к уроку 2. Далее: Практика к уроку 3 →
| Термин | Жизненная аналогия | Коротко |
|---|---|---|
массив | полка с коробками | упорядоченный набор задач, к каждой можно обратиться по номеру |
filter | ситечко для чая | пропускает только подходящие элементы, количество уменьшается |
map | конвейер / ксерокс | берёт каждый элемент и возвращает преобразованный, число не меняется |
find | поиск по номеру | находит первую подходящую коробку и останавливается (может вернуть undefined) |
reduce | накопительный итог | бежит по всем и складывает результат в одно значение |
типы / interface | бланк анкеты | контракт: какие поля обязательны и какого они типа, ошибку видно до запуска |
Promise / async-await | обещание + ожидание посылки | ждём результат от сервера; try/catch — страховка при сбое |
1. Метод filter сохраняет то же количество элементов, что и в исходном массиве. Верно или неверно?
Ответ: Неверно. filter — как ситечко для чая: пропускает только подходящие задачи, поэтому на выходе их обычно меньше (как в Задании 1, где из 3 задач осталось 2 активных).
2. Если вызвать tasks.find(t => t.id === 999), а задачи с таким номером нет, метод вернёт undefined. Верно или неверно?
Ответ: Верно. find — как поиск коробки по номеру: если на полке нет коробки с нужным номером, он возвращает пустоту (undefined), а не ошибку. Поэтому результат всегда проверяют перед использованием.
3. В TypeScript интерфейс Task помогает поймать опечатку в имени поля (например, titel вместо title) уже после запуска приложения. Верно или неверно?
Ответ: Неверно. Интерфейс — это бланк анкеты: TypeScript проверяет поля ещё ДО запуска и подчеркнёт ошибку сразу, не дожидаясь, пока приложение сломается у пользователя.