Заголовочные файлы
Для установки связи с внешними файлами скриптов javascript в TS служат декларативные или заголовочные файлы. Это файлы с расширением .d.ts , они описывают синтаксис и структуру функций и свойств, которые могут использоваться в программе, не предоставляя при этом конкретной реализации. Их действие во многом похоже на работу файлов с расширением .h в языках C/C++. Они выполняют своего рода роль оберток над библиотеками JavaScript.
Рассмотрим, как мы можем использовать заголовочные файлы. Иногда в программах на javascript используются глобальные переменные, которые должны быть видны для всех функций приложения. Например, пусть на веб-странице (или во внешнем подключаемом файле javascript) в коде js определена переменная:
Metanit.com Приложение на TypeScript
В данном случае для простоты переменная определена веб-странице, хотя она также могла быть определена во внешнем подключенном js-файле.
И, допустим, мы хотим использовать эту переменную message в коде TypeScript в файле app.ts:
console.log(message);
При запуске приложения компилятор TS не сможет скомпилировать программу, так как для кода TS глобальная переменная пока не существует. В этом случае нам надо подключать определение глобальной переменной с помощью декларативных файлов. Для этого добавим в проект новый файл, который назовем globals.d.ts и который будет иметь следующее содержимое:
declare let message: string;
С помощью ключевого слова declare в программу на TS подключается определение глобальной переменной.
То есть у нас получится следующая структура проекта:
- app.ts
- globals.d.ts
- index.html
Компиляция
Если мы компилируем, передавая компилятору в консоли название файла:
tsc app.ts
То в этом случае компилятор не найдет автоматически файл globals.d.ts. В этом случае нам надо в файле app.ts явно указать расположение файла globals.d.ts с помощью директивы reference
/// console.log(message);
Если же мы полагаемся на файл конфигурации tsconfig.json, просто выполняя команду
Подобным образом мы можем подключать другие компоненты кода JavaScript — функции, объекты, классы. Рассмотрим их подключение.
Функции
Пусть на веб-странице в коде js объявлены две следующие функции:
let message = «Hello TypeScript!»; function hello() < console.log(message); >function sum(a, b)
Функция hello() выводит значение переменной message на консоль, а функция sum() возвращает сумму двух чисел.
И, допустим, в коде TS мы хотим вызывать эти функции:
hello(); let result = sum(2, 5); console.log(result);
В этом случае подключение в файле globals.d.ts выглядело бы так:
declare function hello(): void; declare function sum(a: number, b: number): number;
Подключение объектов
Пусть в коде JavaScript есть следующий объект:
const tom = < name: "Tom", age: 37, print()< console.log(`Name: $Age: $`); > >
Используем этот объект в коде typescript:
tom.print();
В этом случае определение объекта в файле globals.d.ts выглядело бы так:
declare const tom: void>;
Подключени сложных объектов
Однако может возникнуть сложность с подключением более сложных объектов. Например, пусть есть такой объект javascript:
var points = [< X: 10, Y: 34 >, < X: 24, Y: 65 >, < X: 89, Y: 12 >];
Для данного массива объектов в файле globals.d.ts мы можем определить соответствующий отдельному объекту интерфейс и подключить массив объектов некоторого интерфейса, который содержит два свойства X и Y:
interface IPoint < X: number; Y: number; >declare var points: IPoint[];
И в TS мы сможем использовать этот массив:
for (let point of points) < console.log(`Точка с координатами X = $Y = $`); >
Консольный вывод браузера:
Точка с координатами X = 10 Y = 34 Точка с координатами X = 24 Y = 65 Точка с координатами X = 89 Y = 12
Подключение классов
Рассмотрим последний пример — подключение в typescript классов, определенных в javascript. Пусть в коде JavaScript определен следующий класс Person:
class Person < constructor(name, age)< this.name = name; this.age = age; >display() < console.log(this.name, this.age); >>
Для этого класса в файле globals.d.ts определим следующее объявление класса:
declare class Person
Для класса прописываем все его поля и методы, при этом методы (в том числе конструктор) не имеют реализации, для них только определяются параметры и их типы и тип возвращаемого значения.
И в коде TypeScript используем этот класс:
let tom = new Person("Tom", 37); tom.display(); // Tom 37 console.log(tom.name); // Tom
Основы TypeScript: компилятор TypeScript (tsc) и tsconfig.json
Добро пожаловать в первую часть серии из десяти частей, посвящённой основам TypeScript. В этой первой части мы рассмотрим компилятор TypeScript ( tsc ) и файл конфигурации ( tsconfig.json ).
Понимание опций и конфигураций компилятора очень важно для оптимизации рабочего процесса TypeScript. Присоединяйтесь к нам, чтобы узнать о различных опциях компилятора, значении tsconfig.json и о том, как использовать эти инструменты для оптимизации процесса TypeScript разработки.
Компилятор TypeScript ( tsc )
1. Что такое tsc
tsc — это компилятор TypeScript. Он принимает TypeScript код (файлы .ts или .tsx ) и компилирует его в JavaScript код (файлы .js ), который может быть выполнен средой выполнения JavaScript.
2. Как установить tsc
Вы можете установить tsc глобально с помощью npm (Node Package Manager/Менеджер пакетов Node), выполнив команду:
npm install -g typescript
3. Компиляция TypeScript кода
После установки tsc вы можете компилировать TypeScript файлы, выполнив следующую команду в терминале:
tsc yourfile.ts
4. Параметры компилятора
tsc поставляется с различными параметрами компилятора, позволяющими настроить процесс компиляции. Например:
tsc - target ES5 - outDir ./dist
Эта команда устанавливает целевую версию ECMAScript на ES5 и указывает каталог для вывода .js файлов как ./dist .
tsconfig.json
1. Что такое tsconfig.json
tsconfig.json — это файл конфигурации для проектов TypeScript. Он позволяет указать параметры компилятора, включить/исключить файлы и настроить другие параметры для вашего TypeScript-проекта.
2. Создание файла tsconfig.json
Вы можете создать файл tsconfig.json вручную в корне проекта или использовать команду tsc —init для генерации базового файла конфигурации.
3. Параметры компилятора в tsconfig.json
В tsconfig.json могут быть включены различные параметры компилятора. К числу распространённых относятся:
- compilerOptions : Определяет параметры компилятора.
- include : Указывает массив шаблонов файлов для включения в компиляцию.
- exclude : Указывает массив шаблонов файлов, которые нужно исключить из компиляции.
- extends : Позволяет расширить другой конфигурационный файл.
4. Пример tsconfig.json
"compilerOptions":
"target": "es5",
"module": "commonjs",
"outDir": "./dist"
>,
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
>
Этот пример устанавливает целевое значение ES5, систему модулей — CommonJS, каталог для вывода — ./dist , включает все файлы TypeScript в каталог src и исключает каталог node_modules .
Использование tsconfig.json позволяет поддерживать согласованную конфигурацию во всем проекте TypeScript и упрощает процесс компиляции, избавляя от необходимости указывать опции при каждом запуске команды tsc .
Основы TypeScript
- Основы TypeScript: компилятор TypeScript ( tsc ) и tsconfig.json
- Основы TypeScript: JavaScript в сравнении с TypeScript
- Основы TypeScript: Примитивы и базовые типы
- Основы TypeScript: Неявные и Явные типы, Утверждения типов
- Основы TypeScript: Создание типов, перечислений и интерфейсов
- Основы TypeScript: Объединение, Литеральные и Размеченные типы
- Основы TypeScript: Типизация функций и сигнатур
- Основы TypeScript: Any , Void , Never , Null , Строгие проверки Null
- Основы TypeScript: ООП практика, классы и наследование
TypeScript: Раскладываем tsconfig по полочкам. Часть 1
Я большой фанат TypeScript. Каждый свой новый проект я предпочитаю писать на нём, а не на чистом JavaScript. В данной статье я не буду рассматривать причины выбора TypeScript или о его преимуществах и недостатках. Я хочу, чтобы данный пост стал своего рода шпаргалкой для тех, кто хочет понять, как настраивать tsconfig , разложить по полочкам его многочисленные флаги и, возможно, узнать некоторые полезные трюки.
Итак, в данной статье я хочу предоставить переработанную и упорядоченную выжимку документации, которая, я уверен, будет полезна тем, кто только начинает свой путь в мире TypeScript или тем, кто до этого момента не нашёл времени и сил, чтобы разобраться в деталях и теперь хочет закрыть этот пробел.
Если открыть официальный референс tsconfig , то там будет полный список всех настроек, разделённых по группам. Однако, это не даёт понимания, с чего начать и что из данного обширного списка опций обязательно, а на что можно не обращать внимания (по крайней мере до поры до времени). Плюс, иногда опции сгруппированы по некому техническому, а не логическому смыслу. Например, некоторые флаги проверок можно найти в группе Strict Checks , некоторые в Linter Checks , а другие в Advanced . Это не всегда удобно для понимания.
Все опции, как и саму статью, я разделил на две группы – базовые и «проверки». В первой части статьи разговор пойдёт про базовые настройки, а во второй уже про различные проверки, т. е. про тюнинг строгости компилятора.
Структура tsconfig
Рассмотрим структуру и некоторые особенности конфига.
- tsconfig.json состоит из двух частей. Какие-то опции необходимо указывать в root , а какие-то в compilerOptions
- tsconfig.json поддерживает комментарии. Такие IDE как WebStorm и Visual Studio Code знают об этом и не выделяют комментарии как синтаксическую ошибку
- tsconfig.json поддерживает наследование. Опции можно разделить по некоторому принципу, описать их в разных файлах и объединить с помощью специальной директивы
Это болванка нашего tsconfig.json :
К root опциям относится только следующие: extends , files , include , exclude , references , typeAcquisition . Из них мы будем рассматривать первые 4. Все остальные опции размещаются в compilerOptions .
Иногда в root секции конфига можно встретить такие опции как compileOnSave и ts-node . Эти опции не являются официальными и используются IDE для своих целей.
Секция root
extends
Type: string | false, default: false.
Указывает путь к файлу из которого нужно унаследовать опции. По большей части, служит инструментом упорядочивания. Можно разделить опции по некой логике, чтобы они не смешивались. Например, вынести настройки строгости в отдельный файл, как в примере болванки конфига. Однако, учитывая поддержку комментариев в tsconfig.json это можно сделать проще:
Рассмотрим другой use-case, где комментариями отделаться не получится. Если необходимо создать production и development конфиги. Так бы мог выглядеть tsconfig-dev.json версия конфига:
В целом, я рекомендую пользоваться extends . Однако, сильно дробить настройки не рекомендую. Это может привести к запутыванию. В том числе по причине того, что множественное наследование не поддерживается.
Если вы решите использовать эту опцию. То увидеть итоговую, объединённую версию конфига поможет команда tsc —showConfig .
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 .
Следующие форматы записей делают одно и тоже 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 .
Версия стандарта 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 подключит поддержку 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 :
outDir
Type: string, default: равняется корневой директории.
Конечная папка, куда будут помещаться собранные артефакты. К ним относятся: .js , .d.ts , и .js.map файлы. Если не указывать значение для данной опции, то все вышеуказанные файлы будут повторять структуру исходных файлов в корне вашего проекта. В таком случае будет сложно удалять предыдущие билды и описывать .gitignore файлы. Да и кодовая база будет похожа на свалку. Советую складывать все артефакты в одну папку, которую легко удалить или заигнорировать системой контроля версий.
Если оставить опцию outDir пустой:
├── 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'; // можно писать такой 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.
Спецификация ES6 добавила новый синтаксис для итерирования: цикл for / of , array spread , arguments spread . Если код проекта преобразовывается в ES5 , то конструкция с циклом for / of будет преобразована в обычный for :
// код es6 const str = 'Hello!'; for (const s of str)
// код es5 без downlevelIteration var str = "Hello!"; for (var _i = 0, str_1 = str; _i < str_1.length; _i++)
Однако, некоторые символы, такие как 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 — это про строгость.
Опции секции 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.
Декоратор — это стандартный паттерн из мира ООП и его можно реализовывать классическим образом, создавая классы или функции-обёртки. Однако, с помощью двух вышеперечисленных флагов можно включить экспериментальный синтаксис декораторов. Данный синтаксис позволяет декорировать классы, их методы и свойства, модификаторы доступа, а так же аргументы функций используя простой и распространённый во многих языках программирования синтаксис @ .
Флаг 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 .
Завершение первой части
В этой статье я расписал самые важные флаги и опции, которые могут понадобиться в подавляющем большинстве проектов. В следующей же части я расскажу про настройку строгости компилятора.
UPD: Здесь можно прочитать вторую часть статьи.
Минимальный проект #
Начнем с создания новой папки. Мы назовем ее proj , однако ей можно дать любое необходимое имя.
mkdir proj cd proj
Структура проекта будет следующей:
proj/ +- src/ +- dist/
Файлы TypeScript будут находиться в папке src , компилироваться с помощью TypeScript, а результат выводиться в dist .
Давайте создадим эту структуру:
mkdir src mkdir dist
Инициализация проекта
Превратим эту папку в npm-пакет.
npm init
Вам зададут несколько вопросов. Для всех можно использовать вариант ответа по умолчанию, кроме вопроса о точке входа ( entry point: ). В качестве точки входа введите ./dist/main.js . Вы всегда можете вернуться и изменить все, что указали, в сгенерированном файле package.json .
Установка зависимостей
Теперь используем npm install , чтобы установить пакеты. В первую очередь глобально установите TypeScript и gulp. (Если вы пользуетесь Unix-системой, то, возможно, команды npm install придется запускать через sudo ).
npm install -g typescript gulp-cli
Затем установите gulp и gulp-typescript в качестве зависимостей времени разработки. Gulp-typescript — это плагин к gulp для поддержки TypeScript.
npm install --save-dev gulp gulp-typescript
Создание простого примера
Давайте напишем несложную программу. Создайте файл main.ts в папке src :
function hello(compiler: string) < console.log(`Привет от $`); > hello("TypeScript");
Создайте файл tsconfig.json в корне проекта proj :
Создание gulpfile.js
Создайте файл gulpfile.js в корне проекта:
var gulp = require("gulp"); var ts = require("gulp-typescript"); var tsProject = ts.createProject("tsconfig.json"); gulp.task("default", function () < return tsProject.src() .pipe(ts(tsProject)) .js.pipe(gulp.dest("dist")); >);
Тестирование полученного приложения
gulp node dist/main.js
Приложение должно вывести в консоль «Привет от TypeScript!».
Добавление модулей #
Прежде чем добраться до Browserify, соберем код и добавим к нему модули. Следующую структуру вы, скорее всего, будете использовать и для настоящих приложений.
Создайте файл src/greet.ts :
export function sayHello(name: string) < return `Привет от $`; >
Теперь измените код в src/main.ts так, чтобы sayHello импортировалась из greet.ts :
import < sayHello >from "./greet"; console.log(sayHello("TypeScript"));
И, наконец, добавьте src/greet.ts в tsconfig.json :
Убедитесь, что модули работают, запустив gulp и проверив работу кода в Node:
gulp node dist/main.js
Обратите внимание, что хотя мы применили синтаксис модулей из ES2015, TypeScript создал модули CommonJS, которые используются в Node. Для целей этого руководства формат CommonJS вполне подходит, но вы можете установить параметр module на объекте настроек, чтобы выбрать формат модулей.
Browserify #
Теперь перенесем проект с Node в браузер. Для этого пришлось бы упаковать все модули в один JavaScript-файл. К счастью, именно это и делает Browserify. Больше того, Browserify позволяет использовать модульную систему CommonJS, которая применяется в Node, и модули для которой по умолчанию создает TypeScript. Это значит, что вносить много изменений в уже настроенную связку TypeScript и Node не потребуется.
Сначала установите browserify, tsify, и vinyl-source-stream. tsify это плагин для Browserify, который, подобно gulp-typescript, предоставляет доступ к компилятору TypeScript. vinyl-source-stream позволяет согласовать файловый вывод Browserify и формат под названием vinyl, который понимает gulp.
npm install --save-dev browserify tsify vinyl-source-stream
Создание страницы
Создайте файл index.html в src :
Теперь измените main.ts , чтобы обновить страницу:
import < sayHello >from "./greet"; function showHello(divName: string, name: string) < const elt = document.getElementById(divName); elt.innerText = sayHello(name); >showHello("greeting", "TypeScript");
При вызове showHello вызывается sayHello , которая изменяет текст параграфа. Теперь замените код в gulpfile.js на следующий:
var gulp = require("gulp"); var browserify = require("browserify"); var source = require('vinyl-source-stream'); var tsify = require("tsify"); var paths = < pages: ['src/*.html'] >; gulp.task("copy-html", function () < return gulp.src(paths.pages) .pipe(gulp.dest("dist")); >); gulp.task("default", ["copy-html"], function () < return browserify(< basedir: '.', debug: true, entries: ['src/main.ts'], cache: <>, packageCache: <> >) .plugin(tsify) .bundle() .pipe(source('bundle.js')) .pipe(gulp.dest("dist")); >);
Здесь создается задача copy-html , которая затем добавляется как зависимость для default . Это значит, что при каждом запуске default сначала будет вызываться copy-html . Кроме того, задача default была изменена так, что вместо gulp-typescript теперь вызывается Browserify с плагином tsify. К счастью, оба эти вызова позволяют передать один и тот же объект параметров для компилятора TypeScript.
После вызова bundle мы используем source (наш псевдоним для vinyl-source-stream), чтобы дать получаемой сборке имя bundle.js .
Протестируйте страницу, запустив gulp и открыв в браузере страницу dist/index.html . На ней должен быть текст «Привет от TypeScript».
Обратите внимание, что мы указали debug: true для Browserify. Это заставляет tsify генерировать карты кода внутри создаваемого файла сборки. Карты кода позволяют отлаживать в браузере не упакованный JavaScript-код, а исходный TypeScript. Можно проверить, работают ли карты кода, открыв в браузере отладчик и установив точку останова в main.ts . При обновлении страницы эта точка останова должна спровоцировать паузу, и браузер позволит отлаживать greet.ts .
Watchify, Babel, и Uglify #
Теперь, когда мы собираем код с Browserify и tsify, можно добавить к сборке различные возможности с помощью плагинов для Browserify.
- Watchify запускает gulp и держит его запущенным, выполняя инкрементальную компиляцию при сохранении какого-либо файла. Это позволяет автоматизировать в браузере цикл «редактирование» — «сохранение» — «обновление».
- Babel представляет собой очень гибкий компилятор, превращающий ES2015 и выше в ES5 и ES3. Это позволяет применять к коду обширные и настраиваемые трансформации, которые не поддерживаются TypeScript.
- Uglify минимизирует код, чтобы он загружался быстрее.
Watchify
Начнем с Watchify и реализуем фоновую компиляцию:
npm install --save-dev watchify gulp-util
Теперь измените gulpfile.js следующим образом:
var gulp = require("gulp"); var browserify = require("browserify"); var source = require('vinyl-source-stream'); var watchify = require("watchify"); var tsify = require("tsify"); var gutil = require("gulp-util"); var paths = < pages: ['src/*.html'] >; var watchedBrowserify = watchify(browserify(< basedir: '.', debug: true, entries: ['src/main.ts'], cache: <>, packageCache: <> >).plugin(tsify)); gulp.task("copy-html", function () < return gulp.src(paths.pages) .pipe(gulp.dest("dist")); >); function bundle() < return watchedBrowserify .bundle() .pipe(source('bundle.js')) .pipe(gulp.dest("dist")); >gulp.task("default", ["copy-html"], bundle); watchedBrowserify.on("update", bundle); watchedBrowserify.on("log", gutil.log);
Здесь, фактически, три изменения, но они требуют небольшой переработки кода.
- Мы «завернули» экземпляр browserify в вызов watchify , и сохранили результат в переменной.
- Вызвали watchedBrowserify.on(«update», bundle); , так что Browserify будет запускать функцию bundle при каждом изменении TypeScript-файлов.
- Вызвали watchedBrowserify.on(«log», gutil.log); для вывода сообщений на консоль.
Из-за (1) и (2) вызов browserify пришлось вынести из задачи default . Кроме того, функции для задачи default пришлось дать имя, так ее будут вызывать и Watchify, и Gulp. Добавление вывода на консоль необязательно, но полезно для отладки.
Теперь при старте Gulp он запустится и останется в запущенном состоянии. Попробуйте изменить код showHello в файле main.ts , и сохранить его. Вы должны будете увидеть похожий вывод:
proj$ gulp [10:34:20] Using gulpfile ~/src/proj/gulpfile.js [10:34:20] Starting 'copy-html'. [10:34:20] Finished 'copy-html' after 26 ms [10:34:20] Starting 'default'. [10:34:21] 2824 bytes written (0.13 seconds) [10:34:21] Finished 'default' after 1.36 s [10:35:22] 2261 bytes written (0.02 seconds) [10:35:24] 2808 bytes written (0.05 seconds)
Uglify
Сначала установите Uglify. Поскольку задача Ugligy заключается в обфускации кода, также придется установить vinyl-buffer и gulp-sourcemaps, чтобы карты кода продолжали работать.
npm install --save-dev gulp-uglify vinyl-buffer gulp-sourcemaps
Теперь измените gulpfile.js следующим образом:
var gulp = require("gulp"); var browserify = require("browserify"); var source = require('vinyl-source-stream'); var tsify = require("tsify"); var uglify = require('gulp-uglify'); var sourcemaps = require('gulp-sourcemaps'); var buffer = require('vinyl-buffer'); var paths = < pages: ['src/*.html'] >; gulp.task("copy-html", function () < return gulp.src(paths.pages) .pipe(gulp.dest("dist")); >); gulp.task("default", ["copy-html"], function () < return browserify(< basedir: '.', debug: true, entries: ['src/main.ts'], cache: <>, packageCache: <> >) .plugin(tsify) .bundle() .pipe(source('bundle.js')) .pipe(buffer()) .pipe(sourcemaps.init()) .pipe(uglify()) .pipe(sourcemaps.write('./')) .pipe(gulp.dest("dist")); >);
Обратите внимание, что для uglify понадобился всего один вызов; вызовы buffer и sourcemaps нужны только для того, чтобы карты кода продолжали работать. Эти вызовы создают отдельный файл с картами кода вместо внедренных в файл, которые были раньше. Теперь можно запустить Gulp и проверить, что из кода в bundle.js получилось нечто нечитаемое:
gulp cat dist/bundle.js
Babel
Для начала установите Babelify. Как и Uglify, Babelify обфусцирует код, поэтому понадобится vinyl-buffer и gulp-sourcemaps.
npm install --save-dev babelify vinyl-buffer gulp-sourcemaps
Теперь измените gulpfile.js следующим образом:
var gulp = require('gulp'); var browserify = require('browserify'); var source = require('vinyl-source-stream'); var tsify = require('tsify'); var sourcemaps = require('gulp-sourcemaps'); var buffer = require('vinyl-buffer'); var paths = < pages: ['src/*.html'] >; gulp.task('copyHtml', function () < return gulp.src(paths.pages) .pipe(gulp.dest('dist')); >); gulp.task('default', ['copyHtml'], function () < return browserify(< basedir: '.', debug: true, entries: ['src/main.ts'], cache: <>, packageCache: <> >) .plugin(tsify) .transform("babelify") .bundle() .pipe(source('bundle.js')) .pipe(buffer()) .pipe(sourcemaps.init()) .pipe(sourcemaps.write('./')) .pipe(gulp.dest('dist')); >);
Также необходимо, чтобы TypeScript генерировал ES2015-код. Тогда Babel превратит созданный TypeScript ES2015-код в ES5. Измените tsconfig.json :
Для столь простого скрипта созданный Babel ES5-код должен быть очень похож на код, созданный TypeScript.
Поддержите перевод документации:
Поддерживатель | Github Репозиторий
Documentation generated by mdoc.