Вы находитесь на странице: 1из 12

9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам.

Часть 1 / Хабр

КАК СТАТЬ АВТОРОМ Тест для аналитиков: какой из вас штурман


barinbritva
13 фев 2021 в 10:03
TypeScript: Раскладываем tsconfig по полочкам. Часть 1
10 мин 69K
Разработка веб-сайтов*, JavaScript*, TypeScript*
Из песочницы

Я большой фанат TypeScript. Каждый свой новый проект я предпочитаю писать на нём, а
не на чистом JavaScript. В данной статье я не буду рассматривать причины выбора
TypeScript или о его преимуществах и недостатках. Я хочу, чтобы данный пост стал
своего рода шпаргалкой для тех, кто хочет понять, как настраивать tsconfig ,
разложить по полочкам его многочисленные флаги и, возможно, узнать некоторые
полезные трюки.
Итак, в данной статье я хочу предоставить переработанную и упорядоченную выжимку
документации, которая, я уверен, будет полезна тем, кто только начинает свой путь в
мире TypeScript или тем, кто до этого момента не нашёл времени и сил, чтобы
разобраться в деталях и теперь хочет закрыть этот пробел.
Если открыть официальный референс tsconfig , то там будет полный список всех
настроек, разделённых по группам. Однако, это не даёт понимания, с чего начать и что
из данного обширного списка опций обязательно, а на что можно не обращать внимания
https://habr.com/ru/articles/542234/ 1/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

(по крайней мере до поры до времени). Плюс, иногда опции сгруппированы по некому
техническому, а не логическому смыслу. Например, некоторые флаги проверок можно
найти в группе Strict Checks , некоторые в Linter Checks , а другие в Advanced .
Это не всегда удобно для понимания.
Все опции, как и саму статью, я разделил на две группы – базовые и "проверки". В
первой части статьи разговор пойдёт про базовые настройки, а во второй уже про
различные проверки, т. е. про тюнинг строгости компилятора.

Структура tsconfig
Рассмотрим структуру и некоторые особенности конфига.
tsconfig.jsonсостоит из двух частей. Какие-то опции необходимо указывать в
root , а какие-то в compilerOptions

tsconfig.json поддерживает комментарии. Такие IDE как WebStorm и Visual


Studio Code знают об этом и не выделяют комментарии как синтаксическую ошибку
tsconfig.json поддерживает наследование. Опции можно разделить по
некоторому принципу, описать их в разных файлах и объединить с помощью
специальной директивы
Это болванка нашего tsconfig.json :

{
// extends позволяет обогатить опции другими опциями из указанного файла
// файлом tsconfig-checks.json займёмся во второй части статьи
"extends": "./tsconfig-checks.json",
// в корне конфига находятся project-specific опции
"compilerOptions": {
// здесь все настройки, связанные с компилятором
}
}

К rootопциям относится только следующие: extends , files , include ,


exclude , references , typeAcquisition . Из них мы будем рассматривать первые
4. Все остальные опции размещаются в compilerOptions .
Иногда в секции конфига можно встретить такие опции как compileOnSave и
root
ts-node . Эти опции не являются официальными и используются IDE для своих целей.

https://habr.com/ru/articles/542234/ 2/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

Секция root

extends

Type: string | false, default: false.


Указывает путь к файлу из которого нужно унаследовать опции. По большей части,
служит инструментом упорядочивания. Можно разделить опции по некой логике, чтобы
они не смешивались. Например, вынести настройки строгости в отдельный файл, как в
примере болванки конфига. Однако, учитывая поддержку комментариев в
tsconfig.json это можно сделать проще:

{
"compilerOptions": {
// блок базовых настроек

// блок настроек строгости


}
}

Рассмотрим другой use-case, где комментариями отделаться не получится. Если


необходимо создать production и development конфиги. Так бы мог выглядеть
tsconfig-dev.json версия конфига:

{
"extends": "./tsconfig.json",
"compilerOptions": {
// переопределяем настройки, которые нужны только для dev режима
"sourceMap": true,
"watch": true
}
}

В целом, я рекомендую пользоваться extends . Однако, сильно дробить настройки не


рекомендую. Это может привести к запутыванию. В том числе по причине того, что
множественное наследование не поддерживается.
Если вы решите использовать эту опцию. То увидеть итоговую, объединённую версию
конфига поможет команда tsc --showConfig .
https://habr.com/ru/articles/542234/ 3/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

files

Type: string[] | false, default: false, связана с include .


Указать список конкретных файлов для компиляции можно использовав данную опцию.

{
"compilerOptions": {},
"files": [
"core.ts",
"app.ts"
]
}

Данная опция подходит лишь для совсем маленьких проектов из нескольких файлов.
include

Type string[], default: зависит от значения files , связана с exclude .


Если опция files не указана, то TypeScript будет использовать эту директиву для
поиска компилируемых файлов. Если include так же не указана, то её значение будет
неявно объявлено как ["**/*"] . Это означает, что поиск файлов будет осуществляться
во всех папках и их подпапках. Такое поведение не оптимально, поэтому в целях
производительности лучше всегда указывать конкретные пути. Можно прописывать как
пути к конкретным файлам, так и паттерны путей.

{
"compilerOptions": {},
"include": [
"src/**/*",
"tests/**/*"
]
}

Если паттерны не указывают конкретных расширений, то TypeScript будет искать файлы


с расширениями .ts , .tsx и .d.ts . А если включен флаг allowJs , то ещё .js и
.jsx .

https://habr.com/ru/articles/542234/ 4/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

Следующие форматы записей делают одно и тоже src , ./src , src/**/* .Я


предпочитаю вариант ./src .
Технически, используя опции include и exclude , TypeScript сгенерирует список всех
подходящих файлов и поместит их в files . Это можно наблюдать если выполнить
команду tsc --showConfig .
exclude

Type: string[], default: ["node_modules", "bower_components", "jspm_packages"].


Директива служит для того, чтобы исключать некоторые лишние пути или файлы,
которые включились директивой include . По умолчанию, опция имеет значение путей
пакетных менеджеров npm , bower и jspm , так как модули в них уже собраны.
Помимо этого, TypeScript будет так же игнорировать папку из опции outDir , если она
указана. Это папка, куда помещаются собранные артефакты сборки. Логично, что их
нужно исключить. Если добавить свои значения в эту опцию, то необходимо не забыть
восстановить умолчания. Так как пользовательские значения не объединяются со
значениями по умолчанию. Другими словами, необходимо вручную указать корень
модулей своего пакетного менеджера.

{
"compilerOptions": {},
"exclude": [
"node_modules",
"./src/**/*.spec.ts"
]
}

Опция exclude не может исключить файлы, указанные через files .


Опция exclude не может исключить файлы, если они импортируются в других файлах,
которые не исключены.

Секция compilerOptions

target

Type: string, default: ES3 , влияет на опции lib , module .


https://habr.com/ru/articles/542234/ 5/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

Версия стандарта ECMAScript, в которую будет скомпилирован код. Здесь большой


выбор: ES3 , ES5 , ES6 (он же ES2015 ), ES2016 , ES2017 , ES2018 , ES2019 ,
ES2020 , ESNext . Для backend приложений/пакетов подойдёт ES6 , если
рассчитываете только на современные версии Node.js и ES5 , если хотите
поддержать более старые версии. На данный момент стандарт ES6 , с небольшими
оговорками, поддерживается 97.29% браузеров. Так что для frontend приложений
ситуация аналогичная.
module

Type: string, default: зависит от target , влияет на опцию moduleResolution .


Модульная система, которую будет использовать ваше собранное приложение. На
выбор: None , CommonJS , AMD , System , UMD , ES6 , ES2015 , ES2020 или
ESNext . Для backend приложений/пакетов подойдёт ES6 или CommonJS в
зависимости от версий Node.js , которые хотите поддерживать. Для frontend
приложений под современные браузеры также подходит ES6 . А для поддержки более
старых браузеров и для изоморфных приложений, определённо стоит выбрать UMD .
Если ваша ситуация не такая простая или хотите знать все тонкости модульных систем,
тогда придётся всё-таки изучить подробную документацию.
moduleResolution

Type: string, default: зависит от module .


Стратегия, которая будет использоваться для импорта модулей. Здесь всего две опции:
node и classic . При этом classic в 99% не будет использоваться, так как это
legacy. Однако, я специально упомянул этот флаг, так как он меняется в зависимости от
предыдущего флага. При изменении значения module режим moduleResolution
может переключиться на classic и в консоли начнут появляться сообщения об
ошибках на строчках с импортами.
Во избежание описанной ситуации, я рекомендую всегда явно указывать значение
node для данного флага.

lib

Type: string[], default: зависит от target .


В зависимости от того какой target установлен в конфиге, TypeScript подключает
тайпинги ( *.d.ts-файлы ) для поддержки соответствующих спецификаций. Например,
если ваш target установлен в ES6 , то TypeScript подключит поддержку
https://habr.com/ru/articles/542234/ 6/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

array.find и прочих вещей, которые есть в стандарте. Но если target стоит ES5 ,
то использовать метод массива find нельзя, так как его не существует в этой версии
JavaScript. Можно подключить полифилы. Однако, для того, чтобы TypeScript понял, что
теперь данную функциональность можно использовать, необходимо подключить
необходимые тайпинги в секции lib . При этом, можно подключить как весь стандарт
ES2015 , так и его часть ES2015.Core (только методы find , findIndex и т. д.).

Конечно, правильным выбором будет подключать тайпинги только той


функциональности, для которой установлены полифилы.

Для --target ES5 подключаются: DOM, ES5, ScriptHost


Для --target ES6: DOM, ES6, DOM.Iterable, ScriptHost

Как только вы что-либо добавляете в lib умолчания сбрасываются. Необходимо


руками добавить то, что нужно, например DOM :

{
"compilerOptions": {
"target": "ES5",
"lib": [
"DOM",
"ES2015.Core"
]
}
}

outDir
Type: string, default: равняется корневой директории.
Конечная папка, куда будут помещаться собранные артефакты. К ним относятся: .js ,
.d.ts , и .js.map файлы. Если не указывать значение для данной опции, то все
вышеуказанные файлы будут повторять структуру исходных файлов в корне вашего
проекта. В таком случае будет сложно удалять предыдущие билды и описывать
.gitignore файлы. Да и кодовая база будет похожа на свалку. Советую складывать
все артефакты в одну папку, которую легко удалить или заигнорировать системой
контроля версий.
Если оставить опцию outDir пустой:
https://habr.com/ru/articles/542234/ 7/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

├── module
│ └── core.js
│ └── core.ts
├── index.js
└── index.ts

Если указать outDir :

├── dist
│ └── module
│ | └── core.js
│ └── index.js
├── module
│ └── core.ts
└── index.ts

outFile

Type: string, default: none.


Судя по описанию, данная опция позволяет объединить все файлы в один. Кажется, что
бандлеры вроде webpack больше не нужны… Однако, опция работает только если
значение module указано None , System или AMD . К огромному сожалению, опция
не будет работать с модулями CommonJS или ES6 . Поэтому скорее всего
использовать outFile не придётся. Так как опция выглядит максимально
привлекательно, но работает не так как ожидается, я решил предупредить вас об этом
гигантском подводном камне.
allowSyntheticDefaultImports

Type: boolean, default: зависит от module или esModuleInterop .


Если какая-либо библиотека не имеет default import , лоадеры вроде ts-loader
или babel-loader автоматически создают их. Однако, d.ts-файлы библиотеки об
этом не знают. Данный флаг говорит компилятору, что можно писать следующим
образом:

// вместо такого импорта


import * as React from 'react';

https://habr.com/ru/articles/542234/ 8/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

// можно писать такой


import React from 'react';

Опция включена по умолчанию, если включен флаг esModuleInterop или module


=== "system".
esModuleInterop
Type: boolean, default: false.
За счёт добавления болерплейта в выходной код, позволяет импортировать CommonJS
пакеты как ES6 .

// библиотека moment экспортируется только как CommonJS


// пытаемся импортировать её как ES6
import Moment from 'moment';

// без флага esModuleInterop результат undefined


console.log(Moment);

// c флагом результат [object Object]


console.log(Moment);

Данный флаг по зависимости активирует allowSyntheticDefaultImports . Вместе


они помогают избавиться от зоопарка разных импортов и писать их единообразно по
всему проекту.
alwaysStrict
Type: boolean, default: зависит от strict .
Компилятор будет парсить код в strict mode и добавлять “use strict” в
выходные файлы.
По умолчанию false, но если включен флаг strict , то true.
downlevelIteration
Type: boolean, default: false.

https://habr.com/ru/articles/542234/ 9/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

Спецификация добавила новый синтаксис для итерирования: цикл for / of ,


ES6
array spread , arguments spread . Если код проекта преобразовывается в ES5 , то
конструкция с циклом for / of будет преобразована в обычный for :

// код es6
const str = 'Hello!';
for (const s of str) {
console.log(s);
}

// код es5 без downlevelIteration


var str = "Hello!";
for (var _i = 0, str_1 = str; _i < str_1.length; _i++) {
var s = str_1[_i];
console.log(s);
}

Однако, некоторые символы, такие как emoji кодируются с помощью двух символов. Т.
е. такое преобразование в некоторых местах будет работать не так, как ожидается.
Включенный флаг downlevelIteration генерирует более многословный и более
"правильный", но менее производительный код. Код получается действительно очень
большим, поэтому не буду занимать место на экране. Если интересно посмотреть
пример, то перейдите в playground и выберете в настройках target -> es5 ,
downlevelIteration -> true .

Для работы данного флага, необходимо, чтобы в браузере была реализация


Symbol.iterator . В противном случае необходимо установить полифил.

forceConsistentCasingInFileNames
Type: boolean, default: false.
Включает режим чувствительности к регистру (case-sensitive) для импорта файлов.
Таким образом, даже в case-insensitive файловых системах при попытке сделать импорт
import FileManager from './FileManager.ts' , если файл в действительности
называется fileManager.ts , приведёт к ошибке. Перестраховаться лишний раз не
повредит. TypeScript - это про строгость.

https://habr.com/ru/articles/542234/ 10/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

Опции секции compilerOptions, которые нужны не в каждом проекте

declaration

Type: boolean, default: false.


С помощью включения данного флага, помимо JavaScript файлов, к ним будут
генерироваться файлы-аннотации, известные как d.ts -файлы или тайпинги. Благодаря
тайпингам становится возможным определение типов для уже скомпилированных js
файлов. Другими словами код попадает в js , а типы в d.ts -файлы. Это полезно в
случае, например, если вы публикуете свой пакет в npm . Такой библиотекой смогут
пользоваться разработчики, которые пишут как на чистом JavaScript, так и на TypeScript.
declarationDir
Type: string, default: none, связан с declaration .
По умолчанию тайпинги генерируются рядом с js -файлами. Используя данную опцию
можно перенаправить все d.ts -файлы в отдельную папку.
emitDeclarationOnly

Type: boolean, default: false, связан с declaration .


Если по какой-то причине вам нужны только d.ts -файлы, то включение данного флага
предотвратит генерацию js -файлов.
allowJs

Type: boolean, default: false.


Портировать ваш JavaScript проект на TypeScript поможет данный флаг. Активировав
allowJs TypeScript компилятор будет обрабатывать не только ts файлы, но и js .
Нет нужды полностью мигрировать проект, прежде чем продолжить его разработку.
Можно это делать файл за файлом, просто меняя расширение и добавляя типизацию. А
новый функционал сразу можно писать на TypeScript.
checkJs

Type: boolean, default: false, связан с allowJs .


TypeScript будет проверять ошибки не только в ts , но и в js -файлах. Помимо
встроенных тайпингов для языковых конструкций JavaScript, TS-компилятор так же
умеет использовать jsDoc для анализа файлов. Я предпочитаю не использовать этот
https://habr.com/ru/articles/542234/ 11/21
9/4/23, 11:25 PM TypeScript: Раскладываем tsconfig по полочкам. Часть 1 / Хабр

флаг, а наводить порядок в коде в момент его типизации. Однако, если в вашем проекте
хорошее покрытие кода jsDoc, стоит попробовать.
С версии 4.1 при включении checkJs , флаг allowJs включается автоматически.
experimentalDecorators и emitDecoratorMetadata

Type: boolean, default: false.


Декоратор - это стандартный паттерн из мира ООП и его можно реализовывать
классическим образом, создавая классы или функции-обёртки. Однако, с помощью двух
вышеперечисленных флагов можно включить экспериментальный синтаксис
декораторов. Данный синтаксис позволяет декорировать классы, их методы и свойства,
модификаторы доступа, а так же аргументы функций используя простой и
распространённый во многих языках программирования синтаксис @ .
Флаг просто активирует синтаксис, а
experimentalDecorators
emitDecoratorMetadata в рантайме предоставляет декораторам дополнительные
мета-данные, с помощью которых можно значительно расширить области применения
данной фичи.
Для работы emitDecoratorMetadata необходимо подтянуть в проект библиотеку
reflect-metadata.
resolveJsonModule
Type: boolean, default: false.
Флаг позволяет включить возможность импортировать *.json файлы. Ничего
дополнительно устанавливать не требуется.

// необходимо указывать расширение .json


import config from './config.json'

jsx
Type: string, default: none.
Если проект использует React, необходимо включить поддержку jsx . В подавляющем
большинстве случаев будет достаточно опций react или react-native . Так же есть
возможность оставить jsx-код нетронутым с помощью опции preserve или
использовать кастомные преобразователи react-jsx и react-jsxdev .
https://habr.com/ru/articles/542234/ 12/21

Вам также может понравиться