Typescript: принудительный импорт

Созданный на 4 сент. 2014  ·  31Комментарии  ·  Источник: microsoft/TypeScript

У меня есть много сценариев, в которых я бы сделал это:

import myExternalModule = require("./myExternalModule");
// not using myExternalModule here

Я не использую myExternalModule в своем коде, но все же хочу, чтобы он был включен с помощью requirejs. Мне просто нужно, чтобы оно было там.
Если бы могло быть ключевое слово forceimport это было бы очень круто!

Question

Самый полезный комментарий

так почему бы просто не добавить

import * as React from "react"; // get the types
import "react";  // just for side effect

в выводе последний импорт: import "react" не будет исключен.

Все 31 Комментарий

Должно быть возможно принудительное излучение, написав

var myExternalModule = require("./myExternalModule");

но это не будет жаловаться, если "./myExternalModule" - неправильный путь.

Правильный подход - ввести фиктивную ссылку, не имеющую побочных эффектов:

import myExternalModule = require("./myExternalModule"); 
myExternalModule;

Хотя это взлом, маловероятно, что будет введен новый синтаксис для этого довольно узкого варианта использования. См. Цели дизайна.

Если TypeScript проверит путь в var myExternalModule = require("./myExternalModule"); тогда это позволит пользователям принудительно выполнить emit _ получить проверку во время компиляции, не прибегая к взлому.

Однако подход var не импортирует типы из модуля.

Кроме того, при необходимости вы можете использовать атрибут amd-dependency.

С какой целью вы импортируете то, что не используется?

Например, в приложении angular у меня есть некоторые директивы в других файлах. Мне нужно импортировать эти директивы, иначе моя html-разметка не будет работать. Однако я не использую их в JavaScript.
Обычно в angular большинство модулей отделены друг от друга и не зависят друг от друга, но их необходимо импортировать, чтобы все приложение работало.

: +1:
Это меня сбивает с толку.
Почему компилятор TypeScript устраняет его?
Внешний модуль не является модулем без экземпляра.
require имеет побочный эффект.
Предложение import кажется совместимым с требованиями AMD и CommonJS. но это не так.

Эта проблема также раздражает при использовании модулей require (), которые на самом деле ничего не экспортируют, а вместо этого подключают функциональность к глобальным объектам, например es5-shim , es6-shim и многие другие.

Это меня сбивает с толку.
Почему компилятор TypeScript устраняет его?

Это оптимизация, потому что иногда мы хотим импортировать только информацию о типе из модуля:

import foo = require('foo');

function bar(paramOne: foo.ParamOne, paramTwo: foo.ParamTwo){}

это не просто оптимизация. Если вы используете импортированный модуль только для типов (т. Е. Не используется в какой-либо позиции значения), мы не можем создать для него логику require, поскольку модуль может быть внешним модулем только для типов, который не существует во время выполнения. Импорт модуля будет означать ошибку времени выполнения при попытке загрузить несуществующий модуль.

Для этого можно использовать флаг amd-dependency :

/// <amd-dependency path="foo" />

import x = require('foo');

Вместо <amd-dependency> который выглядит как взлом, было бы интересно использовать <reference name="..."> IMO.

@danquirk Возлагать на движок TypeScript ответственность за оптимизацию операторов импорта - дело произвольное и опасное. Есть много шаблонов, которые TypeScript еще не захватывает из JavaScript. Одним из примеров является то, как он не указывает, что this может иметь тип # 229.

Импорт используется для загрузки зависимостей до загрузки текущего кода, и на него нельзя ссылаться напрямую. Зависимости Angular - один пример, а плагины jQuery - другой. Любая библиотека, которая расширяет базовый объект и на которую не ссылаются напрямую, подвержена "функции". Решив произвольно не включать импорт на основе локального статического анализа, вы навязываете шаблон компилятора в стиле C явному намерению разработчика, написавшего оператор импорта. Ожидается, что запись import состоит в том, что он будет включен как зависимость и доступен в локальной области модуля. Любое другое действие является побочным эффектом для естественного ожидания выбора записи import внутри модуля.

Вполне возможно, что в этом случае компилятор TypeScript мог бы сделать меньше и сделать больше.

Следующая версия TypeScript будет поддерживать модули стиля ES6 (изменение в настоящее время находится в master). поэтому, если все, что вы хотите, объявляет зависимость, вы можете использовать:

import "myLib";

Компилятор не исключит этот импорт.

Будет ли устранено существующее непоследовательное поведение или оно останется чем-то интересным для людей? Следует ли добавить в документацию, что это случай, когда import не добавляет файл в качестве зависимости? При сборке модулей AMD TypeScript ///<reference... теперь будет считаться ошибкой?

То, как сейчас работает import - это плохой дизайн. Плохой дизайн порождает двусмысленность. Если дополнительное поведение четко не определено, то, скорее всего, оно будет обнаружено, хотя и является побочным эффектом. Отсюда OP, я и другие комментарии и ошибки, связанные с этим на этом форуме.

Существует практика, которая уже существует при использовании управления зависимостями стиля Common или Require. Реализация import показывает, что она была проигнорирована.

Что касается документации, обратитесь к разделу спецификации разделу 11.2.6 :

Объявление внешнего импорта представлено в сгенерированном JavaScript как переменная, инициализированная вызовом функции 'require', предоставленной хостом системы модуля. Объявление переменной и вызов require генерируются для конкретного импортированного модуля только в том случае, если импортированный модуль или локальный псевдоним (раздел 10.3), который ссылается на импортированный модуль, _ указывается как PrimaryExpression где-то в теле импортирующего модуля. Если на импортированный модуль ссылаются только как на ModuleName или TypeQueryExpression, ничего не испускается_.

/// ссылки - это не то же самое, что импорт. /// ссылки вносят дополнительные объявления в вашу глобальную область видимости, например dom API. это выражение компилятору доверять существованию этих сущностей, и это не влияет на сгенерированный код.

Импорт, с другой стороны, должен генерироваться как операторы require. в случае, если вы используете только импорт в позиции типа, намерение неясно, либо вам нужны только типы (которые являются конструкциями времени разработки, которые не существуют во время выполнения), и в этом случае вы хотите, чтобы импорт был исключен , а если это не так, вы, возможно, импортируете несуществующий модуль только для типов. Или вам тоже нужны побочные эффекты. Для последнего синтаксис import "mod"; кажется более ясным синтаксисом для объявления намерения.

@mhegazy Я не хочу открывать новый выпуск, но это небольшая проблема в мире React.
Рассмотрим приведенный ниже код. При компиляции в js ссылка React удаляется, поскольку она не отображается в коде, и код завершится ошибкой, поскольку от нее зависит ReactRouter . Исправление ... действительно тупо под кодом. Есть ли более разумный способ избавиться от этого? Спасибо.

import * as React from "react"; var temp = React.DOM;
....

// mf("route.schedules", "") // this is to register a translation
anonymousRoutes.route("/schedules", {
  name: "schedules",
  subscriptions: function() {
    this.register("schedules", subscriptions.subscribe("schedules"));
  },
  action: () => {
    ReactLayout.render(ClearLayout, {content: <Book />});
  }
});

FIX;) ​​- EHM

import * as React from "react"; var temp = React.DOM;
...

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

нужен ли ему var "React" или просто побочные эффекты от импорта?

Нужен побочный эффект от импорта. Вар был добавлен только для того, чтобы не удалять импорт в транспилированном коде.

так почему бы просто не добавить

import * as React from "react"; // get the types
import "react";  // just for side effect

в выводе последний импорт: import "react" не будет исключен.

К сожалению, это не работает, и я все еще получаю:

ReferenceError: React is not defined
    at action [as _action] (schedule_router.jsx:15)
    at Route.callAction (kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2306)
    at kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2025
    at Object.Tracker.nonreactive (tracker.js:560)
    at kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2016
    at Tracker.Computation._compute (tracker.js:294)
    at Tracker.Computation._recompute (tracker.js:313)
    at Object.Tracker._runFlush (tracker.js:452)
    at Object.Tracker.flush (tracker.js:412)
    at Router._invalidateTracker (kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2065)

Я думаю, что причина здесь "слабых зависимостей" в Meteor, которые зависят от _callee_, чтобы предоставить для них все необходимые ссылки.

Недавно я начал изучать машинописный текст для Angular 2. Это несколько раз привлекало меня, как, впрочем, и других новичков. Я понимаю, что это оптимизация, но на самом деле это очень сбивает с толку. Если я набираю что-то в своем ts-файле, я ожидаю, что он произведет, выведет или что-то еще, а не просто проигнорирует это. Я понимаю, что могу принудительно создать инструкцию require, но это кажется немного хакерским. Это похоже на причину, по которой я, как пользователь, должен иметь возможность принимать собственные решения без того, чтобы компилятор решал, что лучше.

TypeScript накладывает объявление типа поверх значений, поэтому один и тот же синтаксис импорта дает вам типы, значения или и то, и другое. это делает работу с модулем очень удобной и интуитивно понятной; если вам нужен тип из другого модуля, просто импортируйте его при импорте переменной или просто импортируйте класс и используйте его как тип и как конструктор с new.

С другой стороны, вы можете импортировать несуществующие модули, поскольку они являются просто контейнерами типов. если компилятор выдал ссылки на эти модули, он завершится ошибкой во время выполнения. решение, которое у нас есть, состоит в том, чтобы определить, _ как_ использовался импорт, и _все_ ссылки на него используются как типы, тогда он не нужен во время выполнения и опускается.

очевидно, это может сбивать с толку, если вам также нужны побочные эффекты модуля. но и другой способ сбивает с толку, если был произведен импорт в несуществующий модуль, и пусть сторона теряет удобство объединения типов и значений в один и тот же синтаксис.

Обходной путь:
При использовании традиционного разрешения (для moduleResolution не задано значение node ), оператор импорта, содержащий ! в своем идентификаторе, никогда не будет исключен. Я считаю, что это как-то связано с тем, что мы пытаемся лучше поддерживать поведение systemjs , но его можно использовать для принудительного импорта. Если вы _are_ используете systemjs, requirejs или любой загрузчик, который позволяет переназначать идентификаторы модулей, вы можете завершить принудительный импорт с помощью !js или аналогичной метки и сопоставить его с загрузчиком модуля. TS дословно выдаст импорт (и не будет пытаться проверить тип или разрешить его), и ваш загрузчик можно научить понимать импорт.

Похоже, что команде уже нужно создать специальный код для этих типов импорта, например, в checker.ts:

// If we're compiling under --jsx react, the symbol 'React' should
// be marked as 'used' so we don't incorrectly elide its import. And if there
// is no 'React' symbol in scope, we should issue an error.
if (compilerOptions.jsx === JsxEmit.React) {
    let reactSym = resolveName(node.tagName, "React", SymbolFlags.Value, Diagnostics.Cannot_find_name_0, "React");
    if (reactSym) {
        getSymbolLinks(reactSym).referenced = true;
    }
}

Как указывает @mhegazy , это не просто оптимизация, а скорее, чтобы избежать необходимости импорта без определения времени выполнения (например, интерфейса), что могло бы вызвать ошибку времени выполнения.

Я сомневаюсь, что будет происходить чаще. Вы можете легко определить, является ли импорт * .d.ts, чтобы его можно было легко опустить.

В остальных случаях компилятору кажется довольно тривиальной задачей обнаружить, что импортированный модуль не будет иметь вывода во время выполнения и исключит только этот импорт.

С точки зрения СУХОГО , это предложение заставило меня пожелать, чтобы у меня был транспилятор для вашего транспайлера, который мог бы написать для меня этот дублированный код.

import * as React from "react"; // get the types
import "react";  // just for side effect

На мой взгляд, это должен быть выбор _imported_ файла, нужно ли его загружать во время выполнения (потому что он знает, что у него есть побочные эффекты). Это действительно не должно определяться файлом _loading_ (т.е. файлом с оператором импорта), поскольку это не его дело - знать детали реализации других файлов / модулей.

По крайней мере, это будет хорошо работать с точки зрения angularjs, где файл, который определяет директивы и службы, "знает", что он должен быть загружен во время выполнения, если на него есть ссылка, поэтому компилятор не должен исключать его. Какой-то намек в файле для компилятора машинописного текста на «не исключать» исправит это ИМО. Но я, вероятно, упускаю из виду некоторые варианты использования.

-JM

xmodule.ts:

console.log('ive been imported');
export class X {
}
import {X} from "xmodule"; // get the types
import "xmodule";  // just for side effect

Если в xmodule есть операторы, например, оператор console.log, это по определению не просто куча объявлений. Импортирующий модуль не должен нести ответственность за создание операторов, использующих xmodule, чтобы xmodule считался чем-то большим, чем просто серией объявлений типов.

@weswigham

Вы упомянули использование взрыва для принудительного импорта. Это предпочтительнее для моего сценария. Как мне его использовать? Пробовал все следующее:

!import {Service} from './service';
import! {Service} from './service';
import {!Service} from './service';
import {Service!} from './service';
import {Service} from '!./service';
import {Service} from './service!';
import {Service} from !'./service';
import {Service} from './service'!;

Это было в строке, но я думаю, что это поведение было недавно удалено, когда были введены сопоставления путей.

Я использую библиотеку, например response, для импорта обертки узла - h (если я не ошибаюсь, в response это response ()), поэтому я импортирую вот так:

import { h, Component } from "preact";

Тогда, конечно, у меня нет h во время выполнения. Потому что зачем там быть? Кроме того, использование с сохранением jsx, поэтому TypeScript действительно не о h, но тогда babel знает.

Итак, что осталось, используя ссылку типа h; после импорта да?

Для h (или любой другой настраиваемой фабрики jsx) используйте --jsxFactory чтобы компилятор знал, что это фабрика, которую вы используете во время выполнения. например, --jsxFactory h .

На данный момент не требуется typescript@next Должен быть доступен в TypeScript 2.1.2.

@mhegazy Для проектов, у которых есть два типа потребителей JSX (например, react и snabbdom), это неприемлемо.

Я работаю над проектом, который использует реакцию для рендеринга веб-интерфейса и настраиваемый виртуальный дом, реализованный нами для рендеринга объектов webgl. И это касается крайнего случая, поскольку нам НУЖНЫ два разных типа аннотации @jsx для одного и того же проекта.

Теперь я вынужден экспортировать наш собственный h как глобальную переменную ... Это уродливо.

Включите меня в список разработчиков, которым нужен / нужен однострочный импорт для этого. Две строки исходного кода - это то, что мы пытаемся исключить, особенно когда мы хотим импортировать много модулей подряд. Я думаю, что мне показалось лучше всего ключевое слово / синтаксис для принудительного импорта. Еще раз спасибо, ребята!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

MartynasZilinskas picture MartynasZilinskas  ·  3Комментарии

blendsdk picture blendsdk  ·  3Комментарии

bgrieder picture bgrieder  ·  3Комментарии

kyasbal-1994 picture kyasbal-1994  ·  3Комментарии

DanielRosenwasser picture DanielRosenwasser  ·  3Комментарии