Модули
Глобальный модуль
По умолчанию, когда вы начинаете писать код в новом файле TypeScript, он находится в глобальном пространстве имен. Например, следующий код в foo.ts
:
const foo = 123;
Если вы создадите новый файл bar.ts
в том же проекте, система типов TypeScript позволит вам использовать переменную foo
, как если бы она была доступна глобально:
const bar = foo; // allowed
Использование пространства глобальных переменных опасно, поскольку оно конфликтует с переменными в файле. Мы рекомендуем использовать файловый модуль, указанный ниже.
Файловый модуль
Он также называется внешним модулем. Если вы включите import
или export
на корневом уровне вашего файла TypeScript, он создаст локальную область в этом файле. Поэтому нам нужно изменить вышеприведенный foo.ts
к следующему виду (обратите внимание на использование export
):
export const foo = 123;
В глобальном пространстве имен у нас больше нет переменной foo
, что можно доказать, создав новый файл bar.ts
:
const bar = foo; // ERROR: "cannot find name 'foo'"
Если вы хотите использовать переменные из foo.ts
в bar.ts
, вы должны явно импортировать его и обновить bar.ts
следующим образом:
import { foo } from './foo';
const bar = foo; // allow
Использование import
в файле bar.ts
не только позволяет использовать содержимое, импортированное из других файлов, но и помечает этот файл bar.ts
как модуль, а объявления, определенные в файле, не будут загрязнять глобальное пространство имен.
Детали файлового модуля
Файловый модуль обладает мощными возможностями и удобством использования. Здесь мы обсудим его возможности и некоторые применения.
Пояснения: commonjs, amd, es modules, others
Во-первых, нам нужно прояснить несоответствия этих модульных систем. Я предоставлю вам мой текущий совет и некоторые проблемы.
Вы можете скомпилировать TypeScript в разные типы модулей JavaScript, основываясь на разных параметрах module
. Вот некоторые вещи, которые вы можете игнорировать:
AMD: не используйте его, он работает только в браузере;
SystemJS: Это был хороший эксперимент, и его заменили модули ES;
Модуль ES: не готов.
Хорошей идеей является использование параметра module: commonjs
вместо указанных выше.
Как писать модули TypeScript также сбивает с толку. Сегодня мы должны делать так:
Отказаться от использования синтаксиса
import / require
, т.е.import foo = require ('foo')
Вместо этого используйте синтаксис модуля ES
Круто, давайте посмотрим на синтаксис модуля ES.
Синтаксис модуля ES
Используйте ключевое слово
export
для экспорта переменной (или типа):
// foo.ts
export const someVar = 123;
export type someType = {
foo: string;
};
Экспорт переменной или типа в отдельном операторе
export
:
// foo.ts
const someVar = 123;
type someType = {
type: string;
};
export { someVar, someType };
Вы также можете переименовать переменные для экспорта:
// foo.ts
const someVar = 123;
export { someVar as aDifferentName };
Используйте ключевое слово
import
для импорта переменной или типа:
// bar.ts
import { someVar, someType } from './foo';
Переименуйте импортированные переменные или типы:
// bar.ts
import { someVar as aDifferentName } from './foo';
Помимо указания выходного значения для загрузки, вы также можете использовать глобальную загрузку, то есть указать объект со звездочкой (*), и все выходные значения будут загружены в этот объект:
// bar.ts
import * as foo from './foo';
// Вы можете использовать `foo.someVar` и` foo.someType` и
// любые другие переменные или типы, экспортированные из `foo`
Импортируйте файл только для побочного эффекта с помощью одного оператора import:
import 'core-js'; // стандартная библиотека polyfill
Общий экспорт после импорта из других модулей:
export * from './foo';
Реэкспорт только конкретных из другого модуля
export { someVar } from './foo';
Реэкспорт только конкретных из другого модуля с переименованием
export { someVar as aDifferentName } from './foo';
Импорт / экспорт по умолчанию
Я не люблю использовать экспорт по умолчанию, тем не менее вот синтаксис экспорта по умолчанию:
Использование
export default
:Перед переменной (не нужно использовать
let
/const
/var
);Перед функцией;
Перед классом.
// some var
export default someVar = 123;
// some function
export default function someFunction() {}
// some class
export default class SomeClass {}
При импорте используется
import someName from 'someModule
(вы можете указать любое имя при необходимости):
import someLocalNameForThisFile from './foo';
Модульные пути
Есть два разных вида модулей:
Относительный путь к модулю (путь начинается с
.
(точка), например:./someFile
или../../someFolder/someFile
и т. д.);Другие модули динамического поиска (например:
core-js
,typestyle
,react
или дажеreact/core
и т. д.).
Основное отличие состоит в том, как модуль расположен в файловой системе.
Относительный путь модуля
Это просто, просто следуйте по относительному пути:
Если файл
bar.ts
содержитimport * as foo from './foo'
, то при таком построении файл foo, должен находиться в той же папке;Если файл
bar.ts
содержитimport * as foo from '../foo'
, то при таком построении файл foo, должен находиться в родительском каталоге;Если файл
bar.ts
содержитimport * as foo from '../someFolder/foo'
, папка, в которой находится файлfoo
(someFolder), должна находиться находиться в родительском каталоге.
Или вы можете подумать о других сценариях импорта относительных путей. 😀
Динамический поиск
Когда путь импорта не является относительным путем, в силу вступает поиск модуля nodejs . Я приведу простой пример ниже:
Когда вы используете
import * as foo from 'foo'
, модули ищутся в следующем порядке:./node_modules/foo
../node_modules/foo
../../node_modules/foo
И так до корня системы
Когда вы используете
import * as foo from 'thing/foo'
, содержимое будет искать в следующем порядке:./node_modules/something/foo
../node_modules/something/foo
../../node_modules/something/foo
И так до корня системы
Что такое `place`
Когда я упоминаю проверяемое place
, я хочу сказать, что в этом place
TypeScript проверит следующее (например, местоположение foo):
Если это
place
представляет файл, такой как:foo.ts
, ура!В противном случае, если это
place
является папкой и существует файлfoo/index.ts
, ура!В противном случае, если это
place
является папкой и существует файлfoo/package.json
, и в нем естьtypes
указывающий на типы, ура!В противном случае, если это
place
является папкой и в ней существует файлpackage.json
с указаниемmain
параметра в этом файле, ура!
Под файлом я имею в виду .ts
/ .d.ts
и .js
.
Вот и все, теперь вы эксперт по поиску модулей (это немалый успех).
Переопределить тип динамического поиска
В вашем проекте вы можете объявить глобальный модуль, declate module 'somePath'
для решения проблемы поиска пути к модулю:
// global.d.ts
declare module 'foo' {
// some variable declarations
export var bar: number;
}
а потом:
// anyOtherTsFileInYourProject.ts
import * as foo from 'foo';
// TypeScript предполагает (без поиска), что
// foo это { bar: number }
import/require
только для типов
import/require
только для типовСледующий синтаксис импорта:
import foo = require('foo');
На самом деле он делает только две вещи:
Импортировать всю информацию о типе модуля
foo
;Определите зависимости времени выполнения модуля
foo
Вы можете выбрать, чтобы загружалась только информация о типе и не возникала зависимость во время выполнения. Прежде чем продолжить, вы можете вспомнить раздел пространство деклараций в этой книге.
Если вы не используете импортированное имя в пространстве объявления переменных, то импорт полностью удаляется из сгенерированного JavaScript. Это лучше всего объяснить на примерах. Как только вы поймете это, мы представим вам варианты использования.
Пример 1
import foo = require('foo');
сгенерирует JavaScript:
Это правильно, пустой файл, который не используется.
Пример 2
import foo = require('foo');
let bar: foo;
сгенерирует JavaScript:
let bar;
Это потому, что foo
(или любой другой атрибут, такой как foo.bas
) не используется в качестве переменной.
Пример 3
import foo = require('foo');
const bar = foo;
сгенерирует JavaScript (при условии, что module: commonjs
):
const foo = require('foo');
const bar = foo;
Это потому, что foo
используется как переменная.
Пример использования: ленивая загрузка
Вывод типа должен быть сделан заранее. Это означает, что если вы хотите использовать какой-либо тип из файла foo
в файле , вам нужно будет сделать:
import foo = require('foo');
let bar: foo.SomeType;
Однако в некоторых сценариях вы хотите загружать модуль foo
только тогда, когда это необходимо, и вам нужно использовать имя импортированного модуля только в аннотации типа, а не в переменной. Они будут удалены при компиляции в JavaScript. Затем вы можете вручную импортировать нужные вам модули.
import foo = require('foo');
export function loadFoo() {
// Это ленивая загрузка foo, оригинальная загрузка используется только для аннотаций типов
const _foo: typeof foo = require('foo');
// Теперь вы можете использовать `_foo` вместо` foo` в качестве переменной
}
Не менее простой модуль amd
(с использованием requirejs):
import foo = require('foo');
export function loadFoo() {
// Это ленивая загрузка foo, оригинальная загрузка используется только для аннотаций типов
require(['foo'], (_foo: typeof foo) => {
// Теперь вы можете использовать `_foo` вместо` foo` в качестве переменной
});
}
Они обычно используются в следующих сценариях:
В веб-приложении, когда вы загружаете JavaScript по определенному маршруту;
В приложениях
nodejs
, когда вы хотите загрузить только определенные модули, чтобы ускорить запуск.
Пример использования: нарушение круговых зависимостей
Как и в случае использования отложенной загрузки, некоторые загрузчики модулей (commonjs / node
и amd / requirejs
) плохо обрабатывают циклические зависимости. В этом случае, с одной стороны, мы используем ленивый код загрузки, а с другой - полезно предварительно загрузить модуль.
Пример использования: обязательно импортируйте
Иногда вы хотите загрузить файл только для побочного эффекта (например, модуль может зарегистрироваться в некоторой библиотеке, такой как дополнения CodeMirror и т. д.). Однако, если вы просто выполняете import/require
, скомпилированный JavaScript не будет содержать зависимости от модуля, и ваш загрузчик модулей (например, веб-пакет) может полностью игнорировать импорт. В таких случаях вы можете использовать переменную sureImport
, чтобы гарантировать, что скомпилированный JavaScript принимает зависимость от модуля, например:
import foo = require('./foo');
import bar = require('./bar');
import bas = require('./bas');
const ensureImport: any = foo || bar || bas;
global.d.ts
Выше, когда мы обсуждали файловые модули, мы сравнивали глобальные переменные с файловыми модулями, и мы рекомендуем использовать файловые модули, а не загрязнять глобальное пространство имен.
Однако, если в вашей команде есть начинающие TypeScript разработчики, вы можете предоставить им файл global.d.ts
для размещения некоторых интерфейсов или типов в глобальном пространстве имен. Эти определенные интерфейсы и типы могут использоваться во всех ваших TypeScript файлах.
global.d.ts
- отличный способ расширитьlib.d.ts
, если вам нужно.Когда вы переходите с JS на TS, определение модуля объявлений
'some-library-you-dont-care-to-get-defs-for'
поможет вам быстро начать работу.
Last updated
Was this helpful?