Перейти к основному содержимому
Блокчейн19ГЛАВА 19Как запустить код: Node, tsc и первая программа.ts.jsnode .ts601 байт или ничеготри пути

Как запустить код: Node, tsc и первая программа

Прочитано0%
Квизы0/5
Тренажёрыне пройден0/1
Уровень 1 · Новичок

Зачем это нужно​

В Solidity вопрос «как запустить» почти не стоял: контракт компилировался и уезжал в сеть, а вызывал его кто угодно — кошелёк, другой контракт, скрипт.

Обычная программа так не работает. У неё нет сети, которая её подхватит: её запускают руками, командой в терминале. Эта глава — про то, что происходит между строкой в файле и текстом на экране, и почему у одной программы на TypeScript есть три разных способа запуска, отличающихся по времени в тридцать раз.

Node — среда выполнения, и она не блокчейн​

Среда выполнения (runtime) — программа, которая читает ваш код и исполняет его. В Solidity такой средой была виртуальная машина сети: ни файлов, ни исходящих запросов, ни ввода с клавиатуры; время видно только как метка блока, зато есть газ и вечное хранилище.

Node — противоположность:

виртуальная машина сетиNode
файлы, сеть, часынетесть
хранилище между запускамивечноенет: всё живёт до конца процесса
плата за работугазнет
кто запускаеттранзакциячеловек командой

Внутри Node лежит тот же движок, что в браузере, но без страницы и окна: браузер и серверный процесс — две разные обвязки вокруг одного механизма.

Три вещи, которые программа отдаёт наружу​

Обычный вывод. console.log печатает аргументы через пробел и переводит строку. Несколько аргументов писать удобнее, чем склеивать строку плюсом: числа и объекты Node форматирует сам.

Поток ошибок. Туда уходят console.error, предупреждения и все необработанные исключения. Поэтому падение видно на экране, даже когда обычный вывод перенаправлен в файл.

Код возврата. Ноль — «отработала», любое другое число — «упала». На него смотрят все инструменты сборки, и в этой главе он встретится ещё не раз.

Способ первый: компилятор собирает .js, потом Node его запускает​

.ts — то, что пишет человек: там есть аннотации типов и интерфейсы. .js — то, что исполняется: там ничего этого нет. Между ними всегда есть перевод; вопрос только в том, кто его делает и остаётся ли результат на диске.

npx tsc # проверяет типы и пишет app.js рядом
node app.js # запускает то, что получилось

Первая модель проходит все три способа и печатает, что происходит:

до всего: app.js рядом с app.ts есть? false

=== способ 1: tsc собирает .js, потом node запускает .js ===
npx tsc : код возврата 0 , вывод пустой: true
появился app.js : true - 601 байт
node : v24.14.1
tsc : 6.0.3
komissiya: 25

=== способ 2: node app.ts напрямую ===
появился app.js : false
node : v24.14.1
komissiya: 25

=== что дороже: замер этого прогона ===
сборка дольше запуска .js : true
node app.ts дольше node app.js : true
(сами миллисекунды пляшут от прогона к прогону, порядок — нет)

=== способ 3: тот же файл, но срезание типов выключено ===
код возврата 1 - TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"
ТРИ ПУТИ ОТ ФАЙЛА .TS ДО РЕЗУЛЬТАТАapp.tstscapp.js · 601 Бтипы проверены, файл остался на дискеnode app.tsтипы срезаны, но НИЧЕГО не проверенобез срезания типовERR_UNKNOWN_FILE_EXTENSION ".ts"сборка дороже запуска готового файла, а прямой запуск дороже запуска .js — на срезание типовтолько первый путь оставляет артефакт на диске: после прямого запуска рядом не появляется ничегои только первый проверяет типы: код возврата 0 у компилятора означает «типы сошлись»
Только сборка проверяет типы и оставляет файл на диске; прямой запуск не делает ни того, ни другого

Компилятор даёт две вещи, которых нет у остальных способов. Первая — проверка типов: код возврата 0 означает «типы сошлись». Вторая — файл на диске, который можно положить в образ и запускать там, где компилятора нет вовсе.

Способ второй: node запускает .ts напрямую​

Современный Node умеет читать .ts сам: он вырезает из текста всё типовое и исполняет остаток. Никаких настроек не нужно — просто node app.ts.

Ключевая оговорка, ради которой этот раздел и написан: Node ничего не проверяет. Он не знает про компилятор, не читает его настройки и с удовольствием запустит файл, который компилятор отверг. Это мы увидим в третьей модели.

Важно

Прямой запуск удобен для скриптов: развернуть контракт, посчитать балансы, дёрнуть узел. Для кода, который сдаётся, проверку типов всё равно запускают отдельно — иначе смысл языка теряется.

Чего прямой запуск не умеет​

Срезание типов работает там, где удаление символов оставляет годный код. Четыре конструкции этому не подчиняются, потому что требуют генерации кода. Модель запускает каждую:

enum-demo.ts -> код 1 | SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]:
TypeScript enum is not supported in strip-only mode
ns-demo.ts -> код 1 | TypeScript namespace declaration is not supported…
pp-demo.ts -> код 1 | TypeScript parameter property is not supported…
ia-demo.ts -> код 1 | TypeScript import equals declaration is not supported…

=== те же файлы, но через компилятор ===
node enum-demo.js : код 0 , напечатано: 0 Lender

Падение происходит до первой строки выполнения: это отказ прочитать файл, а не ошибка в программе.

Лечится двумя способами: собрать компилятором — или не пользоваться этими четырьмя конструкциями в скриптах. Второе проще: перечисление заменяется набором строковых значений, поле в конструкторе пишется длинной записью.

Отвечено вопросов: 0 из 2
Что делает node app.ts с типами?
Почему enum не запускается напрямую?
Отвечено верно: 0 из 2

Что остаётся после компиляции​

Вторая модель компилирует файл и печатает результат целиком:

sample.ts: 21 строк, 768 символов
sample.js: 19 строк, 579 символов

=== сгенерированный sample.js целиком ===
var Role;
(function (Role) {
Role["Lender"] = "lender";
Role["Borrower"] = "borrower";
})(Role || (Role = {}));
class Vault {
total;
constructor(total) {
this.total = total;
}
}
const age = 17;
const wallet = { address: '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B' };
const vault = new Vault(1000);
…

=== что печатает запуск собранного .js ===
typeof age : number
typeof wallet : object
typeof Role : object -> lender
typeof Vault : function -> 1000

=== поиск имён в готовом .js ===
Age вычеркнут компилятором
Wallet вычеркнут компилятором
Role остался в .js
Vault остался в .js
ЧТО ИСЧЕЗАЕТ ПРИ КОМПИЛЯЦИИ, А ЧТО ОСТАЁТСЯисчезает бесследно· псевдоним типа· интерфейс· аннотации типов у переменных· типы параметров и возвратаостаётся настоящим кодом· class — становится функцией· enum — становится объектом· параметр-свойство — присваиванием· namespace — объектом с кодом21 строка и 768 символов исходника превратились в 19 строк и 579 символовровно то, что остаётся кодом, прямой запуск и не умеет: удалить это, не сгенерировав ничего, нельзяпроверка типа во время работы даёт «number», а не имя вашего типа: имени уже нет
Типы исчезают бесследно; перечисление и класс остаются настоящим кодом

Два вывода.

Типы существуют только до запуска. Псевдоним типа и интерфейс вычеркнуты — в готовом файле их нет вообще. Проверка типа во время работы даёт number, а не имя вашего типа: имени уже не существует.

Для человека, пришедшего из Solidity, это переворот. Там struct — это ещё и раскладка в хранилище, реальная вещь. Здесь тип — заметка для компилятора, которая исчезает.

Не всё исчезает. Перечисление превратилось в объект, класс — в функцию. Ровно то, что остаётся кодом, прямой запуск и не умеет: удалить это, ничего не сгенерировав, нельзя.

Три вида ошибок и кто их ловит​

Третья модель кладёт в одну папку три сломанные программы:

=== проверка типов до запуска: npx tsc --noEmit по всей папке ===
код возврата 2
bad-type.ts(2,7): error TS2322: Type 'string' is not assignable to type 'number'.

=== ошибка 1: несовместимый тип. Что скажет node? ===
node bad-type.ts: код возврата 0
zalog plus sto = 1500100

=== ошибка 2: во время выполнения ===
node bad-runtime.ts: код возврата 1
TypeError: Cannot read properties of undefined (reading 'lltv')
at lltvOf (file://bad-runtime.ts:13:25)

=== ошибка 3: логическая ===
node bad-logic.ts: код возврата 0
LTV : 0.9
порог LLTV : 75
ликвидировать? false
а на самом деле 90 >= 75 -> true

=== итог ===
tsc поймал ошибок: 1
упало при запуске : 1 из 3
ТРИ ВИДА ОШИБОК — И КТО ИХ ЛОВИТнесовместимый типпоймал: TS2322запустился, код 0склеил строку с числомобращение к несуществующемумолчитупал: TypeErrorвидно только в запускеневерное условиемолчитотработал, код 0напечатал неправдукомпиляторзапусккомпилятор поймал одну из трёх, запуск уронил одну — третью не поймал никто
Одну ошибку поймал компилятор, одну — запуск, третью не поймал никто

Самое поучительное — первая строка результата. Компилятор отверг файл с кодом возврата 2, а Node запустил его и напечатал 1500100: строка «1500» склеилась с числом 100 вместо сложения. Программа отработала успешно и выдала бессмыслицу.

Вторая ошибка видна только в запуске: обращение к несуществующему элементу. Знакомое Cannot read properties of undefined — самая частая ошибка в этом языке вообще, и следующая глава целиком про то, откуда она берётся.

Третья не ловится ничем. Условие ликвидации перепутано, типы сошлись, программа не упала — и напечатала неправду. Такие ошибки ловят только тестами и сверкой чисел.

Отвечено вопросов: 0 из 2
Компилятор отверг файл с TS2322, а node его запустил и напечатал 1500100. Что произошло?
Какую из трёх ошибок не поймает ни компилятор, ни запуск?
Отвечено верно: 0 из 2

Паспорт проекта: package.json​

Файл создаётся одной командой и описывает проект: имя, версия, зависимости и короткие команды. На запуск влияют два поля.

Тип модулей. Строка "type": "module" переключает проект на современный способ подключения файлов. Без неё Node сначала пробует старый формат и печатает предупреждение, которое прямо подсказывает починку.

Зависимости для разработчика. Компилятор и прочие инструменты ставятся отдельно от того, что нужно самой программе, — командой с ключом -D.

Рядом появляются папка с установленными пакетами и файл с точными версиями всего дерева. Правило: описание проекта и файл версий кладут в репозиторий, папку с пакетами — никогда; её восстанавливают одной командой.

Интересный факт

Установка компилятора в живом прогоне создала папку на 26 МБ из 281 файла — при двух строчках в списке зависимостей. Это нормально: пакеты тянут другие пакеты. Именно поэтому папку не кладут в репозиторий и не носят с собой.

Настройки компилятора и строгий режим​

Компилятор читает файл настроек — там указывают, какую версию языка выпускать, куда класть результат и насколько строго проверять.

Одна настройка важнее всех: строгий режим. Он включает набор проверок, главная из которых — запрет молча обращаться к тому, чего может не быть. Без него компилятор пропустит половину ошибок, ради которых язык и берут.

Правило простое: строгий режим включён всегда. Следующая глава показывает, что именно он ловит.

Типичные ошибки​

node app.ts на файле с перечислением. Отказ прочитать файл: конструкция требует генерации кода.

Ожидание, что прямой запуск проверит типы. Не проверит. Проверка — отдельная команда.

Запуск .ts без поддержки срезания типов. ERR_UNKNOWN_FILE_EXTENSION — среда не понимает расширение.

Отсутствие строки про тип модулей. Предупреждение при каждом запуске; текст сам подсказывает починку.

Папка с пакетами в репозитории. Десятки мегабайт и сотни файлов вместо двух строк в описании проекта.

Глобальная установка компилятора вместо локальной. На другой машине версия окажется другой, и сборка поведёт себя иначе.

Компилятор вызывают по полному пути. Есть команда, которая находит локальный инструмент сама.

Игнорирование кода возврата. Сборка «прошла», хотя компилятор вернул 2 — в автоматической проверке это выяснится слишком поздно.

Слова главы​

  • Среда выполнения (runtime) — программа, исполняющая ваш код; здесь это Node.
  • console.log / console.error — обычный вывод и поток ошибок.
  • Код возврата — 0 «отработала», иначе «упала».
  • .ts и .js — исходник человека и то, что исполняется.
  • Компилятор — проверяет типы, стирает их и пишет результат на диск.
  • Срезание типов — прямой запуск .ts: типы вырезаются, ничего не проверяется.
  • Строгий режим — набор проверок компилятора; включён всегда.
  • Описание проекта — имя, версии, команды; тип модулей и зависимости разработчика.
  • Файл версий — фиксирует точные версии всего дерева пакетов.

Тренажёр: набрать вслепую​

Цель: скорость ≥ 100 зн/мин, точность ≥ 88%

npm init -y

На чемпионате​

Модуль А. TypeScript здесь — язык скриптов: развернуть контракты, раздать стартовые балансы, провести шаги демонстрации. Практические выводы:

  • скрипты запускают напрямую — так быстрее и не остаётся мусора рядом с исходниками;
  • проверку типов при этом всё равно запускают отдельно, иначе опечатка выяснится в середине демонстрации;
  • в скриптах не используют перечисления и поля в конструкторе — прямой запуск их не примет;
  • исполняемый файл запуска решения, который требует задание, — это те же команды, собранные в одну.

Проверка площадки в первые минуты. Посмотреть версию среды выполнения и наличие компилятора — одна команда. Если компилятора нет и интернета нет, остаётся прямой запуск: значит, ограничение на четыре конструкции становится обязательным.

Отвечено вопросов: 0 из 2
Почему в скриптах развёртывания лучше не использовать перечисления?
Скрипт запускают напрямую. Нужна ли проверка типов?
Отвечено верно: 0 из 2

Что дальше​

Программа запускается, ошибки видно. Следующая глава — про самое частое падение в этом языке: обращение к тому, чего нет. В Solidity такого не было вовсе — словарь всегда возвращал нули. Здесь появляется идея «значения нет», и у неё два разных обличья.

Отвечено вопросов: 0 из 1
Что остаётся на диске после node app.ts?
Отвечено верно: 0 из 1

Углубиться​

  • Флаг полного преобразования — включает генерацию кода вместо срезания и снимает ограничение на четыре конструкции.
  • Инструменты запуска без сборки — что делали до того, как среда научилась читать .ts, и почему их всё ещё встретишь в чужих проектах.
  • Карты соответствия — как показать номер строки исходника, когда исполняется собранный файл.
  • Команда точной установки — чем отличается от обычной и почему на автоматической проверке используют именно её.
  • Аргументы командной строки — как передать скрипту адрес контракта, не правя исходник.

Сводная проверка по всей главе: 8 вопросов, 5:00 на всё, без подсказок по ходу. Вопросы показываются по одному, ответ изменить нельзя. Если время выйдет — экзамен завершится с тем, что успел ответить. Пересдавать можно сколько угодно раз.

Куда дальше: проверенные ресурсы​

Видео по теме

ВидеоУроки Node js с нуля / #1 – Установка и запуск программы.Программист86
ВидеоTypeScript: первая программа, компиляция ts в js, типы данных: number, string, bКанал Алекса Лущенко
ВидеоЧто такое Node.js?Merion Academy
ВидеоУстановка и конфигурация TypeScript [49] #typescript #javascriptDeveloper In US
ВидеоHow to Setup Node.js with TypeScript in 2023Beyond Fireship

Комментарии

Обсуждение живёт в GitHub Discussions и грузится с чужого домена — поэтому только по нажатию.