Загружаем научный разбор
Подготавливаем текст, источники и редакционные примечания без изменения разметки страницы.
Подготавливаем текст, источники и редакционные примечания без изменения разметки страницы.
Автор: Казачкин Даниил Михайлович · Обновлено
Критический разбор ICSE 2017: 400 исторических ошибок, Flow 0.30 и TypeScript 2.0, обнаружимость 15%, ограничения метода и собственный протокол оценки пользы типов.
Число «15%» легко превращается в обещание: подключим TypeScript и получим на пятнадцать процентов меньше ошибок. Однако для такого вывода нужно знать, что считали ошибкой, как применяли инструмент и какие условия сравнивали. Доля обнаруженных исторических дефектов, снижение числа новых дефектов и экономия времени команды — разные измеряемые величины. Одна не подменяет другую даже тогда, когда все они связаны с полезностью типов.
Разберём конкретную научную публикацию и построим собственный способ использовать её результат при выборе инструментов. Это авторский аналитический материал, а не перевод статьи и не новая репликация её эксперимента. Наши короткие примеры ниже иллюстрируют границы проверки; они не образуют статистическую выборку. Прикладные основания выбора языка разобраны в [уроке JavaScript или TypeScript](/lessons/typescript/typescript-for-developers/typescript-language-choice).
Zheng Gao, Christian Bird и Earl T. Barr представили *To Type or Not to Type: Quantifying Detectable Bugs in JavaScript* на ICSE 2017, страницы 758–769, DOI 10.1109/ICSE.2017.75. Это рецензируемая конференционная работа. Университетский репозиторий UCL подтверждает библиографию и отдельно обозначает размещённый там текст как принятую авторскую версию. Карточка UCL.
Авторы изучили 400 публичных ошибок JavaScript из историй проектов GitHub. Они возвращались к коду перед исправлением, вручную добавляли согласованные аннотации и проверяли диагностику Flow 0.30 и TypeScript 2.0. После разбора оставшихся неопределённых случаев каждый инструмент обнаружил 60 ошибок, то есть 15%; объединение результатов составило 63 ошибки. Поэтому проценты двух инструментов складывать нельзя. Первичный текст исследования, разделы II–IV.
Критерий требовал аннотаций, совместимых с намеренным корректным поведением. Нельзя специально объявить число строкой лишь для получения ошибки компилятора. Метод использовал известное исправление для локализации анализа; среди ограничений авторы обсуждают выбранный корпус публичных ошибок, ручную интерпретацию и возможный пропуск полезных аннотаций вне области исправления. Работа оценивает обнаружимость в таком протоколе, а не причинный эффект внедрения инструмента в случайно выбранную команду. Первичный текст, разделы II и VI.
Рассмотрим собственный мысленный пример. Команда за месяц допустила сто промежуточных ошибок в редакторе, исправила восемьдесят до коммита, ещё пятнадцать заметила на ревью и пять выпустила. Доля предупреждений среди всех промежуточных ошибок и доля среди пяти выпущенных считаются по разным наборам. Инструмент может быть полезным на первом этапе, хотя почти не находит ошибок из последнего набора. Возможна и обратная ситуация.
Эти числа придуманы только для объяснения знаменателя. Они не описывают выборку Gao и коллег и не являются оценкой типичной команды. Их задача — показать, почему перед переносом процента нужно назвать популяцию. Публичный исправленный дефект наблюдаем в истории, а ошибка, удалённая разработчиком до сохранения файла, может не оставить следа. Смешав эти уровни, мы начнём оценивать разные процессы одним числом.
Для решения о проекте полезно сначала задать собственный вопрос. «Какие из недавних инцидентов могли бы дать типовую диагностику?» подходит для ретроспективного разбора. «Станут ли новые задачи выполняться быстрее?» требует наблюдать разработку. «Уменьшится ли ущерб от ошибок?» требует учитывать тяжесть и пользователей. Ни один из вопросов не исчерпывается общим числом срабатываний компилятора.
Предупреждение инструмента становится предотвращённой ошибкой только при дополнительных условиях. Проверка должна запускаться до релевантной границы выпуска, сообщение должно относиться к дефекту, разработчик должен понять его и выполнить корректное исправление. Если команда постоянно подавляет диагностику или проверяет лишь часть файлов, сама выразительность системы типов не обеспечивает этот путь.
Представим два процесса с одинаковым кодом. В первом проверка типов блокирует сборку и поддерживается в рабочем состоянии. Во втором накоплено несколько тысяч предупреждений, а новый сигнал теряется среди старых. Сравнение только версии TypeScript пропустит существенную разницу. Это наш вывод о применении инструмента: результат зависит не только от способности обнаружить нарушение, но и от организации обратной связи.
Отсюда следует более точное толкование ретроспективного опыта. Он может продемонстрировать, что для исторического кода существует полезный договор о типах. Он не восстанавливает автоматически решение, которое реальный разработчик принял бы заранее, и не измеряет все затраты на поддержку этого договора. Для продуктового решения эти вопросы остаются самостоятельными.
Проверим две разные ошибки в небольшом расчёте количества страниц. Предметное требование: положительное число элементов делится на положительный размер страницы с округлением вверх. Сначала нарушим тип аргумента, затем формулу. Примеры рассчитаны на проверку TypeScript со strict и не используют исторический компилятор 2.0.
function pageCount(total: number, pageSize: number): number {
return Math.ceil(total / pageSize);
}
// @ts-expect-error: строка не соответствует договору параметра
const rejected = pageCount('21', 10);
function wrongPageCount(total: number, pageSize: number): number {
return Math.floor(total / pageSize);
}
console.log(pageCount(21, 10), wrongPageCount(21, 10)); // 3 2Первая ошибка диагностируется благодаря явному договору числовых параметров. Во втором случае все типы согласованы, но результат нарушает условие задачи. Для него нужен содержательный тест, например ожидаемое значение 3 для 21 элемента. Так можно увидеть различие механизмов без обобщения по единственному примеру. Компилятор проверяет форму допустимых операций, тест наблюдает выбранное поведение.
Дополнительно есть нулевой размер страницы и отрицательное количество элементов. Тип number допускает такие значения, а также специальные числовые значения JavaScript. Значит, входные ограничения требуют проверки и ясной политики ошибки. Можно описывать уточнённые модели, но место установления соответствующей гарантии всё равно должно существовать. Называть все ошибки числовой функции «ошибками типов» было бы слишком широким толкованием результата.
Даже одинаковая аннотация в разных режимах может дать разную диагностику. Исторические release notes TypeScript 2.0 описывают появление режима strictNullChecks, который отделяет null и undefined от обычных типов. Нельзя читать название версии, не учитывая режим проверки. При воспроизведении исторического результата необходимо сохранить конфигурацию вместе с исходным кодом. TypeScript 2.0: strictNullChecks.
В современном проекте отдельный пример даёт noUncheckedIndexedAccess: обращение к произвольному ключу словаря начинает учитывать отсутствие записи. Это не дополнительная runtime-проверка, а изменение статической информации для вызывающего кода. TSConfig: noUncheckedIndexedAccess.
const pageSizes: Record<string, number> = { compact: 10 };
const selected = pageSizes['unknown'];
// @ts-expect-error: с noUncheckedIndexedAccess возможно undefined
const size: number = selected;
console.log(selected); // undefinedСмена флага может расширить набор предупреждений на выбранном корпусе. Однако нельзя из этого вывести новый процент для всех ошибок: другие дефекты по-прежнему могут находиться за границей типов, а часть предупреждений потребует анализа. Для сравнения версий нужно повторить заранее определённый протокол, указать настройки и показать, какие именно случаи поменяли классификацию.
Вариант с переименованием файлов, большим количеством any и утверждений типа отличается от варианта с проверенными границами данных. Третий путь — проверять существующий JavaScript с JSDoc. Официальная документация описывает особенности проверки .js и отличия от .ts; расширение файла само по себе не характеризует всю строгость анализа. TypeScript: Type Checking JavaScript Files.
Поэтому небольшой пилот следует описывать через конкретные изменения. Например: типизировать входную команду, проверить ответ внешней системы как unknown, убрать подавление в одном критическом модуле и запускать проверку перед слиянием. Тогда понятны и границы работы, и место ожидаемой пользы. Формулировка «внедрили TypeScript на десять процентов» не объясняет, какие гарантии появились у потребителей.
Даже строгая конфигурация не делает TypeScript полностью состоятельной системой доказательства безопасности программ. В документации совместимости открыто описаны практические допущения. Поэтому современный пример с удачной диагностикой не является основанием отменять тесты, проверку внешних данных или ревью предметной логики. TypeScript: A Note on Soundness.
Ниже предложен собственный протокол для учебного или рабочего проекта. Это не описание процедуры авторов ICSE и не её точная репликация. Выберите небольшой период истории до просмотра результатов и включите все подходящие исправления по заранее записанному правилу. Для каждого сохраните версию до исправления, ожидаемое поведение и минимальный воспроизводимый сценарий. Не удаляйте неудобные случаи молча: фиксируйте причину исключения.
Затем попробуйте выразить договор без изменения алгоритма. Проверяйте, относится ли диагностика к изучаемой ошибке, а не к несовместимости старой зависимости. Отдельно записывайте случаи, где решение осталось неясным. Полезно дать второму человеку независимо прочитать несколько примеров: расхождение покажет, какие формулировки требований слишком расплывчаты. Такой контроль улучшает собственный анализ, даже если выборка слишком мала для статистического обобщения.
Итоговая таблица может содержать идентификатор дефекта, нарушенное условие, добавленный договор, диагностируемую строку, затраченное время и оставшийся runtime-тест. Не стоит объединять время установки старого проекта с временем понимания аннотации без отдельного учёта. Это разные расходы, и в будущем проекте они могут соотноситься иначе.
Современные примеры этой статьи автоматически проверены 8 сентября 2026 года на TypeScript 5.9.3 и 6.0.3 с strict, exactOptionalPropertyTypes и noUncheckedIndexedAccess. Отрицательные случаи вызвали диагностику, исполнение вывело 3 2 и undefined. Flow 0.30 и TypeScript 2.0 для этой проверки не запускались; исторические 15% взяты из первичного исследования, а не пересчитаны по нашим двум фрагментам.
Работа 2017 года даёт конкретный пример эмпирического изучения возможностей статической проверки. Её ценность для студента шире одного процента: она заставляет определить наблюдаемую ошибку, договор и критерий полезной диагностики. Собственные три коротких примера из статьи показывают, как задавать эти вопросы, но не заменяют корпус реальных дефектов.
Для продолжения возьмите один баг учебного приложения и оформите мини-отчёт: ожидаемое поведение, ошибка до исправления, информация, доступная типам, и проверка после исправления. [Урок о tsconfig](/lessons/typescript/typescript-for-developers/typescript-tsconfig) поможет зафиксировать режим анализа, [урок об unknown](/lessons/typescript/typescript-for-developers/typescript-any-unknown-never) — честно обозначить неизвестные данные, а [урок о runtime-валидации](/lessons/typescript/typescript-for-developers/typescript-runtime-validation) — установить границу доверия. Вывод должен отвечать за изученный случай и явно оставлять открытым то, что опыт не измерял.
Авторский русскоязычный разбор. Перечисленные первичные источники использованы для проверки технических утверждений и библиографии; материал не является переводом или перепечаткой источников. Собственные примеры и рассуждения отделены от опубликованных эмпирических результатов.
Иллюстрации, таблицы и код первоисточников не воспроизводятся. Примеры и текстовые схемы созданы для этой статьи. Ссылки и библиографические сведения не предоставляют прав на перепубликацию материалов источников.