TypeScript не заменяет JavaScript, а проверяет отношения между данными до запуска программы. Перенесём знакомый список задач в .ts, опишем объект Task, включим strict и увидим, как компилятор останавливает строку вместо boolean ещё до открытия браузера.
Изучать типы на переменных name: string скучно, потому что непонятно, какую ошибку они предотвращают. В мини-проекте связь видна сразу. Задача имеет id, title и done. Функция addTask должна принимать строку, toggleTask работает с числовым id, а render получает массив Task. Если перепутать поля, компилятор покажет строку.
type Task фиксирует форму одной задачи и назначение каждого поля.
strict и noEmitOnError запрещают сборку, когда отношения типов нарушены.
tsc создаёт dist/main.js, а браузер исполняет обычный JavaScript.
Что TypeScript добавляет к JavaScript
Проект содержит index.html, src/main.ts и tsconfig.json. DOM-элементы ищутся через querySelector с generic-параметром, но результат всё равно может быть null. Функция must
| Этап | Что происходит | Признак результата |
|---|---|---|
| Исходник | Разработчик пишет main.ts и тип Task | Редактор знает поля |
| Проверка | tsc анализирует присваивания и вызовы | Ошибка имеет файл и строку |
| Сборка | Типы удаляются, создаётся dist/main.js | Браузер получает JavaScript |
| Выполнение | DOM и пользовательский ввод работают обычно | Список реагирует на события |
У проекта есть один главный маршрут: получить входные данные, проверить их, выполнить действие и показать результат. Если каждый этап можно проверить отдельно, ошибка перестаёт быть загадкой.

От входных данных до видимого результата. Типы исчезают после проверки, но найденные ошибки остаются исправленными
Переносим список задач в main.ts
Создайте каталог, выполните npm init -y и npm install -D typescript. Положите main.ts в src, настройте tsconfig.json и запустите npx tsc. В index.html подключите ./dist/main.js с type="module". Для автоматической пересборки используйте npx tsc --watch. Не подключайте .ts напрямую в обычный браузер.
type Task = {
id: number;
title: string;
done: boolean;
};
function must<T extends Element>(value: T | null, name: string): T {
if (!value) throw new Error("Не найден элемент: " + name);
return value;
}
const form = must(document.querySelector<HTMLFormElement>("#task-form"), "form");
const input = must(document.querySelector<HTMLInputElement>("#task-title"), "input");
const list = must(document.querySelector<HTMLUListElement>("#task-list"), "list");
const tasks: Task[] = [];
function addTask(title: string): void {
tasks.push({ id: Date.now(), title, done: false });
render();
}
function render(): void {
list.replaceChildren(...tasks.map((task) => {
const item = document.createElement("li");
item.textContent = (task.done ? "✓" : "○") + " " + task.title;
item.addEventListener("click", () => {
task.done = !task.done;
render();
});
return item;
}));
}
form.addEventListener("submit", (event) => {
event.preventDefault();
const title = input.value.trim();
if (title) addTask(title);
form.reset();
});- npx tsc завершается без ошибок и создаёт dist/main.js.
- Форма добавляет Task с числовым id, строковым title и boolean done.
- Клик по li переключает done, а render обновляет значок.
- Присваивание task.done = "да" останавливает сборку с диагностикой типа.
Большая часть типов выводится автоматически. Мы явно описываем форму данных и важные границы, но не пишем аннотацию у каждой локальной переменной. Так код остаётся читаемым, а компилятор получает достаточно информации.

Что отличает устойчивый проект от случайного успеха. Аннотации нужны там, где связывают части программы
Как компилятор выводит типы и сужает null
TypeScript строит модель программы до запуска. Когда tasks объявлен как Task[], push обязан получить объект с нужными полями. Когда task.done имеет boolean, оператор ! возвращает boolean, а строка запрещена. Generic в querySelector сообщает ожидаемый класс элемента, но не отменяет null: DOM может не содержать селектор.
| Часть | Ответственность | Что проверить |
|---|---|---|
| type Task | Фиксирует форму доменных данных | Все поля имеют назначение |
| Inference | Выводит тип локальных значений | Нет лишних аннотаций |
| Generic | Связывает querySelector с видом элемента | Методы input доступны редактору |
| Narrowing | Убирает null после явной проверки | DOM-ошибка остаётся честной |
| tsconfig | Задаёт strict и границы сборки | Ошибка блокирует emit |
Типы существуют только во время разработки. Если localStorage, fetch или пользовательская форма возвращают неожиданные данные, оператор as Task не проверит объект. Нужна runtime-валидация или собственная функция-предикат. Избегайте any: он отключает важную часть проверок и распространяется дальше по коду.
Создаём ошибку и читаем диагностику tsc
Соберите проект и добавьте две задачи. Затем намеренно замените done: false на done: "нет" и прочитайте диагностику tsc. Верните boolean. Удалите #task-list из HTML и запустите страницу: must должен дать понятную runtime-ошибку. После этого добавьте функцию toggleTask(id: number): void и перенесите переключение из render. Попробуйте вызвать её строкой.
- Соберите проект в dist и откройте список задач.
- Создайте ошибку string вместо boolean и найдите строку диагностики.
- Удалите DOM-элемент и проверьте runtime-защиту must.
- Вынесите переключение в typed-функцию toggleTask.
- Добавьте union type для трёх фильтров.
- Загрузите JSON из localStorage и проверьте его поля до as.
- tsc проходит только для согласованного кода.
- DOM null обрабатывается явно.
- Task используется во всех функциях списка.
- Неверный фильтр ловится до запуска.
Типы полезны, когда вы видите предотвращённую ошибку. Строка вместо boolean, null вместо элемента и неправильное имя фильтра больше не ждут случайного клика пользователя.

Четыре проверки перед следующим шагом. Каждая проверка предотвращает конкретный класс ошибки
Добавляем типы там, где они окупаются
Дальше вынесите Task и Filter в types.ts, добавьте модуль хранения и тест чистых функций. Для сетевых данных подключите схему runtime-валидации или напишите type guard. Не начинайте с сложных generics и utility types: сначала получите пользу от объектов, union, параметров и возвращаемых значений.
Как изучить тему в Кодике
В Кодике сначала повторите объекты, массивы, функции и DOM в JavaScript. Затем перенесите один небольшой файл в TypeScript и включите strict. Каждую новую конструкцию связывайте с конкретной ошибкой проекта.
| Шаг | Что изучить в Кодике | Мини-проверка |
|---|---|---|
| 1 | Объекты и массивы JavaScript | Собрать tasks без типов |
| 2 | Object type и функции | Описать Task и addTask |
| 3 | DOM null и narrowing | Написать must без non-null assertion |
| 4 | Union и literal types | Ограничить фильтр тремя значениями |
| 5 | Runtime-границы | Проверить JSON из localStorage |
Не пытайтесь запомнить весь API. В тренажёре Кодика сначала воспроизведите чистую логику без библиотеки, затем восстановите подключение и обработку ошибок. Финальный тест: объяснить проект по памяти и добавить одну свою функцию.
any отключает проверки. Начните с unknown на внешней границе и сузьте значение условиями.
!Non-null assertion скрывает риск. Проверьте элемент и дайте понятную ошибку.
.ts подключают прямо в браузерСначала запустите tsc или сборщик и подключайте созданный JavaScript.
Assertion не меняет данные во время выполнения. Проверяйте JSON отдельно.
Что получится в итоге

Один TypeScript-файл описывает Task, безопасно находит DOM и не выпускает сборку, если boolean, id или фильтр получили неверный тип.
TypeScript выполняется в браузере?
Нет. Компилятор проверяет .ts и создаёт обычный JavaScript, из которого типы удалены.
Почему querySelector всё равно возвращает null?
Тип элемента известен, но наличие узла зависит от HTML во время выполнения.
Чем unknown лучше any для внешнего JSON?
unknown требует проверить значение перед использованием, а any отключает проверки.
Как понять, что проект действительно работает?
Проверьте основной сценарий, неверный ввод, повторный запуск и один граничный случай. Затем объясните путь данных своими словами.
Можно ли начать с готового кода из статьи?
Да. Добейтесь результата, измените одно правило и соберите ключевой файл заново без копирования.
Сначала закрепите проектную основу в курсе JavaScript. Возьмите знакомый Todo List или калькулятор и перенесите оди
Ошибки DOM сравните со статьёй про null. В Кодике отдельно перепишите тип Task, must и union фильтра по памяти.