lib.d.ts¶
Специальный файл объявления lib.d.ts
добавляется в каждую сборку TypeScript. Этот файл содержит объявления среды для различных распространенных конструкций JavaScript, присутствующих во время выполнения JavaScript и DOM.
- Этот файл автоматически включается в контекст компиляции проекта TypeScript.
- Цель этого файла - упростить процесс написания кода JavaScript с контролем типов.
Вы можете исключить этот файл из контекста компиляции, указав флаг командной строки компилятора --noLib
(или "noLib" : true
в tsconfig.json
).
Пример использования¶
Как всегда, давайте рассмотрим примеры использования этого файла:
1 2 |
|
Этот код выполняет проверку типа правильно поскольку функция toString
определена в lib.d.ts
для всех объектов JavaScript.
Если вы используете тот же пример кода с опцией noLib
, вы получите ошибку проверки типа:
1 2 3 |
|
Итак, теперь, когда вы понимаете важность lib.d.ts
, как выглядит его содержимое? Мы рассмотрим это дальше.
lib.d.ts
взгляд изнутри¶
Содержимое lib.d.ts
- это прежде всего набор переменных объявлений, например. window
, document
, math
и куча похожих объявлений интерфейсов, например Window
, Document
, Math
.
Простейший способ прочитать документацию и описания типов глобального содержимого - это ввести код, который вы знаете как работает, например Math.floor
и затем F12 (перейти к определению) с использованием вашей IDE (VSCode имеет отличную поддержку этого).
Давайте рассмотрим пример объявления переменной, например window
определяется как:
1 |
|
Это просто declare var
, за которым следует имя переменной (здесь window
) и интерфейс для описания типа (здесь интерфейс Window
). Эти переменные обычно указывают на некоторый глобальный интерфейс, например вот небольшой пример (на самом деле довольно большого) интерфейса Window
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Вы можете видеть, что в этих интерфейсах есть много информации о типах. В отсутствие TypeScript вам нужно было бы держать это в своей голове. Теперь вы можете перенести эти знания в компилятор и сделать их легкодоступными, используя такие вещи, как intellisense
.
Есть веская причина использования интерфейсов для этих глобальных переменных. Это позволяет вам добавлять дополнительные свойства к этим глобальным переменным без необходимости изменять lib.d.ts
. Мы рассмотрим эту концепцию дальше.
Изменение нативных типов¶
Поскольку интерфейс
в TypeScript является расширяемым, это означает, что вы можете просто добавить элементы к интерфейсам, объявленным в lib.d.ts
, и TypeScript подхватит эти добавления. Обратите внимание, что вам нужно сделать эти изменения в глобальном модуле, чтобы эти интерфейсы были связаны с lib.d.ts
. Для этой цели мы даже рекомендуем создать специальный файл с именем global.d.ts
.
Вот несколько примеров, в которых мы добавляем элементы в window
, Math
, Date
:
Пример window
¶
Просто добавьте элементы в интерфейс Window
, например:
1 2 3 |
|
Это позволит вам обезопасить себя проверкой типов:
1 2 3 4 5 6 7 |
|
Пример Math
¶
Глобальная переменная Math
определена в lib.d.ts
в виде (опять же, используйте ваши инструменты разработчика для перехода к определению):
1 2 3 |
|
т.е. переменная Math
является экземпляром интерфейса Math
. Интерфейс Math
определяется как:
1 2 3 4 5 |
|
Это означает, что если вы хотите добавить элементы в глобальную переменную Math
, вам просто нужно добавить его в глобальный интерфейс Math
, например, рассмотрим проект seedrandom
project, который добавляет функцию seedrandom
к глобальному объекту Math
. Она может быть объявлена довольно легко:
1 2 3 |
|
И далее вы можете просто использовать её:
1 2 3 |
|
Пример Date
¶
Если вы посмотрите на определение переменной Date
в lib.d.ts
, вы увидите:
1 |
|
Интерфейс DateConstructor
похож на то, что вы видели ранее с Math
и Window
, в том, что он содержит элементы, которые вы можете использовать вне глобальной переменной Date
, например, Date.now()
. В дополнение к этим элементам он содержит сигнатуры конструктора, которые позволяют вам создавать экземпляры Date
(например new Date()
). Фрагмент интерфейса DateConstructor
показан ниже:
1 2 3 4 5 6 7 |
|
Рассмотрим проект datejs
. DateJS добавляет элементы как в глобальную переменную Date
, так и в экземпляры Date
. Поэтому определения TypeScript для этой библиотеки будет выглядеть следующим образом (Кстати, сообщество уже написало их для вас в этом случае):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Это позволяет вам делать следующее с проверкой типов:
1 2 |
|
Пример string
¶
Если вы поищите информацию о string в lib.d.ts
, вы обнаружите нечто похожее на то, что мы видели для Date
(глобальная переменная String
, интерфейс StringConstructor
, интерфейс String
). Однако следует отметить, что интерфейс String
также влияет на строковые литералы, как показано в следующем примере кода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Подобные переменные и интерфейсы существуют и для других вещей, которые имеют как статические, так и методы экземпляров, такие как Number
, Boolean
, RegExp
и т.д., и эти интерфейсы также влияют на литеральные экземпляры этих типов.
Пример global из модуля¶
Мы рекомендуем создать global.d.ts
для удобства поддержки. Однако вы можете проникнуть в глобальное пространство имен изнутри файлового модуля, если захотите. Это делается с помощью declare global { /*global namespace here*/ }
. Например. предыдущий пример также можно сделать так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Использование своего собственного lib.d.ts¶
Как мы упоминали ранее, использование логического флага компилятора --noLib
заставляет TypeScript исключить автоматическое добавление lib.d.ts
. Есть несколько причин, почему это может быть полезной функцией. Вот некоторые из распространенных:
- Вы работаете в пользовательской среде JavaScript, которая значительно отличается от стандартной среды выполнения на основе браузера.
- Вам нравится иметь строгий контроль над глобальными переменными, доступными в вашем коде. Например lib.d.ts определяет
item
как глобальную переменную, а вы не хотите, чтобы это попадало в ваш код.
После того как вы убрали стандартный lib.d.ts, вы можете добавить файл с аналогичным именем в контекст компиляции, и TypeScript подхватит его для проверки типов.
Примечание: будьте осторожны с
--noLib
. Как только вы окажетесь на территории noLib, если вы решите поделиться своим проектом с другими, они будут тоже вынуждены попасть на территорию noLib (или, скорее, на территорию вашей lib). Еще хуже ситуация, если вы добавите их код в ваш проект, вам может понадобиться портировать его в код на основе вашей lib.
Эффект зависимости lib.d.ts
от параметров вывода¶
Настройка компилятора с выводом в es6
заставляет lib.d.ts
включать дополнительные объявления среды для более современных (es6) вещей, таких как Promise
. Этот особый эффект меняет окружение кода и желаем для некоторых разработчиков, а для других он проблематичен, поскольку он связывает генерацию кода со средой кода.
Однако, если вы хотите более детальный контроль над своей средой, вы должны использовать параметр --lib
, который мы обсудим далее.
lib опция¶
Иногда (довольно часто) вы хотите разорвать связь между выводом компиляции (сгенерированной версией JavaScript) и поддержкой окружающих библиотек. Типичным примером является Promise
, например, сегодня (в июне 2016 года) вы, скорее всего, захотите --target es5
, но по-прежнему будете использовать новейшие функции, такие как Promise
. Для поддержки этого вы можете получить явный контроль над lib
с помощью опции компилятора lib
.
Примечание: использование
--lib
позволяет отделить любую lib магию от--target
, обеспечивая вам лучший контроль.
Вы можете использовать эту опцию в командной строке или в tsconfig.json
(рекомендуется):
Командная строка:
1 |
|
tsconfig.json:
1 2 3 |
|
libs можно классифицировать следующим образом:
- JavaScript версия:
- es5
- es6
- es2015
- es7
- es2016
- es2017
- esnext
- Среда выполнения
- dom
- dom.iterable
- webworker
- scripthost
- ESNext по отдельным фичам (меньше, чем версия)
- es2015.core
- es2015.collection
- es2015.generator
- es2015.iterable
- es2015.promise
- es2015.proxy
- es2015.reflect
- es2015.symbol
- es2015.symbol.wellknown
- es2016.array.include
- es2017.object
- es2017.sharedmemory
- esnext.asynciterable
ПРИМЕЧАНИЕ: опция
--lib
обеспечивает чрезвычайно гибкий контроль. Так что, скорее всего вы хотите выбрать отдельные фичи из версий + среду выполнения. Если --lib не указан, будет добавлена lib по умолчанию:
- Для --target es5 => es5, dom, scripthost
- Для --target es6 => es6, dom, dom.iterable, scripthost
Моя личная рекомендация:
1 2 3 4 |
|
Пример для Symbol с ES5:
Symbol API не добавляется, когда вывод в es5. На самом деле мы получаем ошибку вроде: [ts] Не удается найти имя 'Symbol'. Мы можем использовать "target": "es5" в сочетании с "lib" для предоставления Symbol API в TypeScript:
1 2 3 4 |
|
Polyfill для старых движков JavaScript¶
Существует довольно много функций среды выполнения, таких как Map
/ Set
и даже Promise
(этот список, конечно, со временем изменится), которые вы можете использовать с современными опциями lib
. Чтобы использовать их, все, что вам нужно сделать, это использовать core-js
. Просто установите:
1 |
|
И добавьте импорт в точку входа вашего приложения:
1 |
|
И он должен заполифиллить эти функции среды выполнения для вас 🌹.