Инструменты пользователя

Инструменты сайта


typescript:tsconfig.json

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
typescript:tsconfig.json [2022/11/14 14:10]
werwolf
typescript:tsconfig.json [2023/01/12 12:18] (текущий)
Строка 1: Строка 1:
-=====tsconfig.json=====+===== Структура ​tsconfig =====
  
 +Рассмотрим структуру и некоторые особенности конфига.
 +
 +  * ''​tsconfig.json''​ состоит из двух частей. Какие-то опции необходимо указывать в ''​root'',​ а какие-то в ''​compilerOptions'' ​
 +  * ''​tsconfig.json''​ поддерживает комментарии. Такие IDE как WebStorm и Visual Studio Code знают об этом и не выделяют комментарии как синтаксическую ошибку ​
 +  * ''​tsconfig.json''​ поддерживает наследование. Опции можно разделить по некоторому принципу,​ описать их в разных файлах и объединить с помощью специальной директивы ​
 +
 +Это болванка нашего ''​tsconfig.json'':​
 +
 +<code javascript>​
 +{
 +  // extends позволяет обогатить опции другими опциями из указанного файла
 +  // файлом tsconfig-checks.json займёмся во второй части статьи
 +  "​extends":​ "​./​tsconfig-checks.json",​
 +  // в корне конфига находятся project-specific опции
 +  "​compilerOptions":​ {
 +    // здесь все настройки,​ связанные с компилятором
 +  }
 +}
 +</​code>​
 +
 +К ''​root''​ опциям относится только следующие:​ ''​extends'',​ ''​files'',​ ''​include'',​ ''​exclude'',​ ''​references'',​ ''​typeAcquisition''​. Из них мы будем рассматривать первые 4. Все остальные опции размещаются в ''​compilerOptions''​.
 +
 +Иногда в ''​root''​ секции конфига можно встретить такие опции как ''​compileOnSave''​ и ''​ts-node''​. Эти опции не являются официальными и используются IDE для своих целей.
 +
 +===== Секция root =====
 +
 +==== extends ====
 +
 +**Type: string | false, default: false.**
 +
 +Указывает путь к файлу из которого нужно унаследовать опции. По большей части, служит инструментом упорядочивания. Можно разделить опции по некой логике,​ чтобы они не смешивались. Например,​ вынести настройки строгости в отдельный файл, как в примере болванки конфига. Однако,​ учитывая поддержку комментариев в ''​tsconfig.json''​ это можно сделать проще:
 +
 +<code javascript>​
 +{
 +  "​compilerOptions":​ {
 +    // блок базовых настроек
 +
 +    // блок настроек строгости
 +  }
 +}
 +</​code>​
 +
 +Рассмотрим другой use-case, где комментариями отделаться не получится. Если необходимо создать production и development конфиги. Так бы мог выглядеть ''​tsconfig-dev.json''​ версия конфига:​
 +
 +<code javascript>​
 +{
 +  "​extends":​ "​./​tsconfig.json",​
 +  "​compilerOptions":​ {
 +    // переопределяем настройки,​ которые нужны только для dev режима
 +    "​sourceMap":​ true,
 +    "​watch":​ true
 +  }
 +}
 +</​code>​
 +
 +В целом, я рекомендую пользоваться ''​extends''​. Однако,​ сильно дробить настройки не рекомендую. Это может привести к запутыванию. В том числе по причине того, что множественное наследование не поддерживается.
 +
 +Если вы решите использовать эту опцию. То увидеть итоговую,​ объединённую версию конфига поможет команда ''​tsc --showConfig''​.
 +
 +==== files ====
 +
 +**Type: string[] | false, default: false, связана с** ''​include''​**.**
 +
 +Указать список конкретных файлов для компиляции можно использовав данную опцию.
 +
 +<code javascript>​
 +{
 +  "​compilerOptions":​ {},
 +  "​files":​ [
 +    "​core.ts",​
 +    "​app.ts"​
 +  ]
 +}
 +</​code>​
 +
 +Данная опция подходит лишь для совсем маленьких проектов из нескольких файлов.
 +
 +==== include ====
 +
 +**Type string[], default: зависит от значения** ''​files''​**,​ связана с** ''​exclude''​**.**
 +
 +Если опция ''​files''​ не указана,​ то TypeScript будет использовать эту директиву для поиска компилируемых файлов. Если ''​include''​ так же не указана,​ то её значение будет неявно объявлено как ''​["​%%**/​*%%"​]''​. Это означает,​ что поиск файлов будет осуществляться во всех папках и их подпапках. Такое поведение не оптимально,​ поэтому в целях производительности лучше всегда указывать конкретные пути. Можно прописывать как пути к конкретным файлам,​ так и паттерны путей.
 +
 +<code javascript>​
 +{
 +  "​compilerOptions":​ {},
 +  "​include":​ [
 +    "​src/​**/​*",​
 +    "​tests/​**/​*"​
 +  ]
 +}
 +</​code>​
 +
 +Если паттерны не указывают конкретных расширений,​ то TypeScript будет искать файлы с расширениями ''​.ts'',​ ''​.tsx''​ и ''​.d.ts''​. А если включен флаг ''​allowJs'',​ то ещё ''​.js''​ и ''​.jsx''​.
 +
 +Следующие форматы записей делают одно и тоже ''​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'',​ если она указана. Это папка, куда помещаются собранные артефакты сборки. Логично,​ что их нужно исключить. Если добавить свои значения в эту опцию, то необходимо не забыть восстановить умолчания. Так как пользовательские значения не объединяются со значениями по умолчанию. Другими словами,​ необходимо вручную указать корень модулей своего пакетного менеджера.
 +
 +<code javascript>​
 +{
 +  "​compilerOptions":​ {},
 +  "​exclude":​ [
 +    "​node_modules",​
 +    "​./​src/​**/​*.spec.ts"​
 +  ]
 +}
 +</​code>​
 +
 +Опция ''​exclude''​ не может исключить файлы, указанные через ''​files''​.
 +
 +Опция ''​exclude''​ не может исключить файлы, если они импортируются в других файлах,​ которые не исключены.
 +
 +===== Секция compilerOptions =====
 +
 +==== target ====
 +
 +**Type: string, default:** ''​ES3''​**,​ влияет на опции** ''​lib''​**,​** ''​module''​**.**
 +
 +Версия стандарта ECMAScript, в которую будет скомпилирован код. Здесь большой выбор: ''​ES3'',​ ''​ES5'',​ ''​ES6''​ (он же ''​ES2015''​),​ ''​ES2016'',​ ''​ES2017'',​ ''​ES2018'',​ ''​ES2019'',​ ''​ES2020'',​ ''​ESNext''​. Для backend приложений/​пакетов подойдёт ''​ES6'',​ если рассчитываете только на современные версии ''​Node.js''​ и ''​ES5'',​ если хотите поддержать более старые версии. На данный момент стандарт ''​ES6'',​ с небольшими оговорками,​ поддерживается [[https://​caniuse.com/​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''​.
 +
 +Если ваша ситуация не такая простая или хотите знать все тонкости модульных систем,​ тогда придётся всё-таки изучить [[https://​www.typescriptlang.org/​docs/​handbook/​modules.html|подробную документацию]].
 +
 +==== 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 подключит поддержку ''​array.find''​ и прочих вещей, которые есть в стандарте. Но если ''​target''​ стоит ''​ES5'',​ то использовать метод массива ''​find''​ нельзя,​ так как его не существует в этой версии JavaScript. Можно подключить полифилы. Однако,​ для того, чтобы TypeScript понял, что теперь данную функциональность можно использовать,​ необходимо подключить необходимые тайпинги в секции ''​lib''​. При этом, можно подключить как весь стандарт ''​ES2015'',​ так и его часть ''​ES2015.Core''​ (только методы ''​find'',​ ''​findIndex''​ и т. д.).
 +
 +Конечно,​ правильным выбором будет подключать тайпинги только той функциональности,​ для которой установлены полифилы.
 +
 +<code javascript>​
 +Для --target ES5 подключаются:​ DOM, ES5, ScriptHost
 +Для --target ES6: DOM, ES6, DOM.Iterable,​ ScriptHost
 +</​code>​
 +
 +Как только вы что-либо добавляете в ''​lib''​ умолчания сбрасываются. Необходимо руками добавить то, что нужно, например ''​DOM'':​
 +
 +<code javascript>​
 +{
 +  "​compilerOptions":​ {
 +    "​target":​ "​ES5",​
 +    "​lib":​ [
 +      "​DOM",​
 +      "​ES2015.Core"​
 +    ]
 +  }
 +}
 +</​code>​
 +
 +==== outDir ====
 +
 +**Type: string, default: равняется корневой директории.**
 +
 +Конечная папка, куда будут помещаться собранные артефакты. К ним относятся:​ ''​.js'',​ ''​.d.ts'',​ и ''​.js.map''​ файлы. Если не указывать значение для данной опции, то все вышеуказанные файлы будут повторять структуру исходных файлов в корне вашего проекта. В таком случае будет сложно удалять предыдущие билды и описывать ''​.gitignore''​ файлы. Да и кодовая база будет похожа на свалку. Советую складывать все артефакты в одну папку, которую легко удалить или заигнорировать системой контроля версий.
 +
 +Если оставить опцию ''​outDir''​ пустой:​
 +
 +<​code>​
 +├── module
 +│   ​└── core.js
 +│   ​└── core.ts
 +├── index.js
 +└── index.ts
 +</​code>​
 +
 +Если указать ''​outDir'':​
 +
 +<​code>​
 +├── dist
 +│   ​└── module
 +│   ​| ​  ​└── core.js
 +│   ​└── index.js
 +├── module
 +│   ​└── core.ts
 +└── index.ts
 +</​code>​
 +
 +==== 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-файлы''​ библиотеки об этом не знают. Данный флаг говорит компилятору,​ что можно писать следующим образом:​
 +
 +<code javascript>​
 +// вместо такого импорта
 +import * as React from '​react';​
 +// можно писать такой
 +import React from '​react';​
 +</​code>​
 +
 +Опция включена по умолчанию,​ если включен флаг ''​esModuleInterop''​ или ''​module''​ %%===%% "​system"​.
 +
 +==== esModuleInterop ====
 +
 +**Type: boolean, default: false.**
 +
 +За счёт добавления болерплейта в выходной код, позволяет импортировать ''​CommonJS''​ пакеты как ''​ES6''​.
 +
 +<code javascript>​
 +// библиотека moment экспортируется только как CommonJS
 +// пытаемся импортировать её как ES6
 +import Moment from '​moment';​
 +
 +// без флага esModuleInterop результат undefined
 +console.log(Moment);​
 +
 +// c флагом результат [object Object]
 +console.log(Moment);​
 +</​code>​
 +
 +Данный флаг по зависимости активирует ''​allowSyntheticDefaultImports''​. Вместе они помогают избавиться от зоопарка разных импортов и писать их единообразно по всему проекту.
 +
 +==== alwaysStrict ====
 +
 +**Type: boolean, default: зависит от** ''​strict''​**.**
 +
 +Компилятор будет парсить код в ''​strict mode''​ и добавлять ''​“use strict”''​ в выходные файлы.
 +
 +По умолчанию false, но если включен флаг ''​strict'',​ то true.
 +
 +==== downlevelIteration ====
 +
 +**Type: boolean, default: false.**
 +
 +Спецификация ''​ES6''​ добавила новый синтаксис для итерирования:​ цикл ''​for / of'',​ ''​array spread'',​ ''​arguments spread''​. Если код проекта преобразовывается в ''​ES5'',​ то конструкция с циклом ''​for / of''​ будет преобразована в обычный ''​for'':​
 +
 +<code javascript>​
 +// код es6
 +const str = '​Hello!';​
 +for (const s of str) {
 +  console.log(s);​
 +}
 +</​code>​
 +
 +<code javascript>​
 +// код es5 без downlevelIteration
 +var str = "​Hello!";​
 +for (var _i = 0, str_1 = str; _i &lt; str_1.length;​ _i++) {
 +  var s = str_1[_i];
 +  console.log(s);​
 +}
 +</​code>​
 +
 +Однако,​ некоторые символы,​ такие как ''​emoji''​ кодируются с помощью двух символов. Т. е. такое преобразование в некоторых местах будет работать не так, как ожидается. Включенный флаг ''​downlevelIteration''​ генерирует более многословный и более "​правильный",​ но менее производительный код. Код получается действительно очень большим,​ поэтому не буду занимать место на экране. Если интересно посмотреть пример,​ то перейдите в [[https://​www.typescriptlang.org/​play?#​code/​MYewdgzgLgBNBOMC8MBEAJApgG2yAhKgNwBQAZiIgBSiSwQwhlxTwCUMA3iTDLRCGyYAdHgDmVCG1IBfIA|playground]] и выберете в настройках ''​target -> es5'',​ ''​downlevelIteration -> true''​.
 +
 +Для работы данного флага, необходимо,​ чтобы в браузере была реализация ''​Symbol.iterator''​. В противном случае необходимо установить полифил.
 +
 +==== forceConsistentCasingInFileNames ====
 +
 +**Type: boolean, default: false.**
 +
 +Включает режим чувствительности к регистру (case-sensitive) для импорта файлов. Таким образом,​ даже в case-insensitive файловых системах при попытке сделать импорт ''​import FileManager from '​./​FileManager.ts''',​ если файл в действительности называется ''​fileManager.ts'',​ приведёт к ошибке. Перестраховаться лишний раз не повредит. TypeScript - это про строгость.
 +
 +===== Опции секции 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 для анализа файлов. Я предпочитаю не использовать этот флаг, а наводить порядок в коде в момент его типизации. Однако,​ если в вашем проекте хорошее покрытие кода jsDoc, стоит попробовать.
 +
 +С версии 4.1 при включении ''​checkJs'',​ флаг ''​allowJs''​ включается автоматически.
 +
 +==== experimentalDecorators и emitDecoratorMetadata ====
 +
 +**Type: boolean, default: false.**
 +
 +''​Декоратор''​ - это стандартный паттерн из мира ООП и его можно реализовывать классическим образом,​ создавая классы или функции-обёртки. Однако,​ с помощью двух вышеперечисленных флагов можно включить экспериментальный синтаксис декораторов. Данный синтаксис позволяет декорировать классы,​ их методы и свойства,​ модификаторы доступа,​ а так же аргументы функций используя простой и распространённый во многих языках программирования [[https://​refactoring.guru/​ru/​design-patterns/​decorator|синтаксис]] ''​@''​.
 +
 +Флаг ''​experimentalDecorators''​ просто активирует синтаксис,​ а ''​emitDecoratorMetadata''​ в рантайме предоставляет декораторам дополнительные мета-данные,​ с помощью которых можно значительно расширить области применения данной фичи.
 +
 +Для работы ''​emitDecoratorMetadata''​ необходимо подтянуть в проект библиотеку [[https://​github.com/​rbuckton/​reflect-metadata|reflect-metadata]].
 +
 +==== resolveJsonModule ====
 +
 +**Type: boolean, default: false.**
 +
 +Флаг позволяет включить возможность импортировать ''​*.json''​ файлы. Ничего дополнительно устанавливать не требуется.
 +
 +<code javascript>​
 +// необходимо указывать расширение .json
 +import config from '​./​config.json'​
 +</​code>​
 +
 +==== jsx ====
 +
 +**Type: string, default: none.**
 +
 +Если проект использует React, необходимо включить поддержку ''​jsx''​. В подавляющем большинстве случаев будет достаточно опций ''​react''​ или ''​react-native''​. Так же есть возможность оставить ''​jsx-код''​ нетронутым с помощью опции ''​preserve''​ или использовать кастомные преобразователи ''​react-jsx''​ и ''​react-jsxdev''​.
 +
 +===== Завершение первой части =====
 +
 +В этой статье я расписал самые важные флаги и опции, которые могут понадобиться в подавляющем большинстве проектов. В следующей же части я расскажу про настройку строгости компилятора.
 +
 +UPD: Здесь можно прочитать [[https://​habr.com/​ru/​post/​557738/​|вторую часть статьи]].
 +
 +=====tsconfig.json=====
 +====config====
 <code javascript>​ <code javascript>​
 // Файл "​tsconfig.json":​ // Файл "​tsconfig.json":​
Строка 153: Строка 502:
  
 </​code>​ </​code>​
 +
typescript/tsconfig.json.1668424258.txt.gz · Последние изменения: 2023/01/12 12:17 (внешнее изменение)