Typescript: TS4023: экспортируемая переменная 'X' имеет или использует имя 'Y' из внешнего модуля 'a / file / path', но не может быть названа

Созданный на 18 нояб. 2015  ·  24Комментарии  ·  Источник: microsoft/TypeScript

Я пытаюсь создать набор внешних модулей TypeScript следующим образом:

// ClassA.ts
export default class ClassA {
  public method() { return true; } 
}

// ModuleB.ts
import ClassA from './ClassA';
var moduleB = {
  ClassA: ClassA
};
export default moduleB;

// ModuleA.ts
import moduleB from './ModuleB';
var moduleA = {
  moduleB: moduleB
}
export default moduleA;

Идея состоит в том, что это будет скомпилировано и предоставлено как библиотека. План состоит в том, что потребитель библиотеки создаст экземпляр ClassA следующим образом:

import moduleA from './ModuleA';
var anInstance = moduleA.moduleB.ClassA();

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

Using tsc v1.6.2
.../src/ModuleA.ts(3,5): error TS4023: Exported variable 'moduleA' has or is using name 'ClassA' from external module ".../src/ClassA" but cannot be named.

Может ли кто-нибудь пролить свет на эту проблему? Имеет ли смысл то, что я пытаюсь сделать?

Question

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

FWIW вы обычно получите более быстрые ответы на Stack Overflow, но мы задаем здесь хорошо сформулированные вопросы.

Проблема здесь в том, что вы используете флаг --declaration , но не предоставили компилятору возможность выполнять свою работу.

При попытке сгенерировать ModuleA.d.ts компилятор должен написать литерал типа объекта (например, { moduleB: { classA: *mumble?* } } ), представляющий форму модуля. Но в области нет имени, которое напрямую ссылается на classA , поэтому это тип «не может быть назван» и возникает ошибка.

Если вы добавите import из ClassA к ModuleA.ts , ошибка должна исчезнуть.

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

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

FWIW вы обычно получите более быстрые ответы на Stack Overflow, но мы задаем здесь хорошо сформулированные вопросы.

Проблема здесь в том, что вы используете флаг --declaration , но не предоставили компилятору возможность выполнять свою работу.

При попытке сгенерировать ModuleA.d.ts компилятор должен написать литерал типа объекта (например, { moduleB: { classA: *mumble?* } } ), представляющий форму модуля. Но в области нет имени, которое напрямую ссылается на classA , поэтому это тип «не может быть назван» и возникает ошибка.

Если вы добавите import из ClassA к ModuleA.ts , ошибка должна исчезнуть.

Привет, Райан - спасибо за совет RE stack overflow и за совет - добавление импорта в ModuleA привело к компиляции, как и ожидалось.

Небольшой контекст здесь - моя цель здесь действительно создать объявления для моих внешних модулей. (Что-то, как мне кажется, еще не совсем работает? # 5039?) Этот импорт может быть полностью необходимым, но для ModuleA кажется немного странным необходимость импортировать символы, которые уже экспортирует moduleB. В результате каждый раз, когда я добавляю экспорт объекта ModuleB , мне нужно будет добавить импорт в ModuleA - либо напрямую:

import {moduleB} from './moduleB';
import {ClassA} from './ClassA';

Или через ModuleB (чтобы уменьшить количество указаний путей для ClassA в обоих местах):

import {moduleB, ClassA} from './moduleB';

Я очень мало знаю о компиляторе TS - возможно, то, что я говорю, совершенно нереалистично, но на первый взгляд кажется, что компилятор может сделать вывод, что ModuleB экспортирует объект с N символами на нем, поэтому ModuleA нужны эти символы? Информация об импорте для ModuleB уже есть - может ли ModuleA использовать это? Или это было бы невозможно, потому что импорт для ClassA является полностью внутренним для ModuleB , поэтому компилятор не может определить тип для ClassA при создании определений для ModuleA ?

Еще раз спасибо за то, что нашли время ответить. Я разобрался со своей проблемой, так что вышесказанное в основном связано с любопытством - не нужно спешить с ответом.

Я не совсем понимаю, что вы говорите. Как должен выглядеть ModuleA.d.ts в этом предложении?

Компилятор не будет добавлять зависимости (т. Е. Операторы импорта), которых не было в пользовательском коде, когда он генерирует объявления. полученная ошибка означает, что компилятор пытается написать аннотацию типа для экспортированного объявления, но не может. это может иметь одну из двух причин: либо имя недоступно, то есть не импортировано в текущий модуль, либо есть объявление, которое заменяет исходное объявление.

в обоих случаях ваша работа - добавить явную аннотацию типа, если вы добавите какую-либо аннотацию типа, она будет дословно выдана на выходе; другой вариант - убедиться, что имя доступно, т.е. у вас есть импорт в модуль, и вы понимаете, что это значит для ваших пользователей, которые импортируют ваш модуль.

@mhegazy сказал:

Компилятор не будет добавлять зависимости (т. Е. Операторы импорта), которых не было в пользовательском коде, когда он генерирует объявления.

Проблема в том, что мне не всегда нужны операторы импорта в коде (очевидно, поскольку он работает без --declarations ), и их включение является шумным, заставляя такие вещи, как tslint жаловаться на «неиспользованный импорт». Я понимаю, почему вы не хотите, чтобы компилятор добавлял зависимости к испускаемому javascript, но в чем проблема с их добавлением к испускающим объявлениям?

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

Это могло иметь более глубокие последствия.

Рассмотрим moduleA -> moduleB -> moduleC -> moduleD .

moduleB - это то, что нужно сделать import { ABC } from 'moduleA' чтобы обойти эту проблему.

Когда moduleC использует moduleB и экспортирует свою подпись, он снова не компилируется, потому что moduleB требует ABC от moduleA но на этот раз moduleB не экспортировал.

Это означает либо:

  1. moduleC должен иметь жесткую зависимость moduleA и импортировать ABC
  2. moduleB необходимо не только импортировать, но и повторно экспортировать ABC а затем moduleC импортирует его.

Если moduleD делает аналогичные вещи, то в основном вам нужно знать всю цепочку зависимостей.

Я не смог проверить это, чтобы доказать, что это так, потому что я использую typings и есть проблема, которая блокирует меня: https://github.com/typings/typings/issues/625 ~~

РЕДАКТИРОВАТЬ: я могу воспроизвести его, и действительно, мой moduleD должен ссылаться на moduleA для импорта.
В моем примере:

  • moduleA => redux
  • moduleB => redux-thunk
  • moduleC => пользовательский код поверх redux-thunk
  • moduleD => некоторая библиотека
  • ABC => Dispatch интерфейс в redux

У меня такая же проблема, описанная @unional

Пожалуйста, откройте эту проблему еще раз, проблемы, описанные @unional , очень реалистичны, и это очень затрудняет добавление каких-либо промежуточных / вспомогательных библиотек поверх библиотек при использовании тех же типов, что и исходный модуль, который мы повторно экспортируем.

Проблема https://github.com/Microsoft/TypeScript/issues/9944 отслеживает добавление импорта на этапе создания объявления.

Спасибо!

Проблема с добавлением импорта заключается в том, что с noUnusedLocals компилятор будет жаловаться на неиспользуемый тип. Я мог бы добавить явную аннотацию типа, но тогда я не получаю вывода. Пример:

class Whatever {
  fetch(uri: string): Promise<void> { }
  ensureFetched = MemoizedFunction<(uri: string) => Promise<void>> = memoize((uri: string) => this.fetch(uri))
}

Я хотел бы опустить аннотацию типа для ensureFetched

Я нашел обходной путь для этого:
в tsconfig: include: [ ..., "node_modules/@your_scope/your_library" ]
удачи и удачи: smiley:

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

@mhegazy проблема воспроизведена в следующем сценарии:

import * as Foo from "./Foo";

export class Bar {
    baz = new Foo.Baz(); // Compiler forgot "Foo." prefix in the type, and throws this error, because "Baz" without perfix is not imported.
    getBaz() { // All the same
       return new Foo.Baz();
    }
}

Решение - указать тип явным образом:

import * as Foo from "./Foo";

export class Bar {
    baz: Foo.Baz = new Foo.Baz(); // ok
    getBaz(): Foo.Baz { // ok
       return new Foo.Baz();
    }
}

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

@PFight Спасибо! Это было для меня!

Я только что столкнулся с этой проблемой при использовании:

export { IMyInterface } from './file'
````

The solution was to do this:
```ts
import { IMyInterface } from './file'
export { IMyInterface }

Но в этом действительно не должно быть необходимости.

Кто-нибудь придумал, как бороться с noUnusedLocals ?

@yordis // @ts-ignore

@pelotom да, это полностью моя вина,

Я думал об одном, а писал о другом.

Устранена ли в Typescript проблема между noUnusedLocals и объявлениями?

Мой текущий обходной путь, который кажется мне полной болью,

import {SomeInterface} from "./SomeFile";

const _dummySomeInterface : undefined|SomeInterface = undefined;
_dummySomeInterface;

//Code that implicitly uses SomeInterface

Избегает noUnusedLocals , разрешает вывод типов и для универсальных интерфейсов, где это возможно.

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

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

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

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

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

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

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