Как мне повторно экспортировать импортированный модуль, в котором есть интерфейсы, удобным способом?

#typescript

#typescript

Вопрос:

У меня был один файл с большим количеством модулей в нем. Я хотел разделить эти модули на разные файлы и предоставить их из исходного файла. Это связано с тем, что исходный файл имеет> 800 loc и становится очень сложным в управлении.

Итак, я создал файл, содержащий модуль с функциями и интерфейсами:

 export module Subsection1 {
    
    export interface Interface {
        id: string;
    }
    
    export function DoInterfaceStuff(obj: any) {
        return = <Interface>{ id: obj }
    }
}
 

У меня есть

 
import { Subsection1 as Subsection1Base } from "./Subsection1";


export module GodManager {

    export type Subsection1 = Subsection1Base amp; (typeof Subsection1Base);
    export var Subsection1 = Subsection1Base;

    var ObjByID = new Map<string, Subsection1.Interface>();
    export function DoThing(source) {
        ObjByID.set(source.id, Subsection1.DoInterfaceStuff(source.origin));
    }
}
 

ПРИМЕЧАНИЕ
Новое имя и переменная должны быть доступны извне для текущего класса, потому что я на самом деле загружаю Subsection1Base лениво (в основном, чтобы избежать циклической зависимости).

Комментарии:

1. Почему вы вызываете как интерфейс, так и модуль (пространство имен) Trigger ? Вместо этого вы могли бы вызвать интерфейс ITrigger . Или вы могли бы поместить интерфейс триггера в модуль триггера.

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

3. Похоже, в typescript уже есть запрос функции именно для этой функции: github.com/microsoft/TypeScript/issues/4336

Ответ №1:

Ответ @Connor Low указал мне правильное направление, выделив правильную ошибку, но это фактическое решение вопроса.

Из этой проблемы github импортированные модули из других файлов могут быть import export Name = Value повторно экспортированы с использованием синтаксиса.

Итак, переписав пример кода, решение:

 import { Subsection1 as Subsection1Base } from "./Subsection1";


export module GodManager {

    export import Subsection1 = Subsection1Base;

    var ObjByID = new Map<string, Subsection1.Interface>();
    export function DoThing(source) {
        ObjByID.set(source.id, Subsection1.DoInterfaceStuff(source.origin));
    }
}
 

И все это работает!

Ответ №2:

Прелюдия

Вопрос изменил код примера, но первоначально они экспортировали module
и interface с тем же именем и попытался сослаться module на контекст, в котором interface действителен только . Хотя не обязательно неправильно разделять имя между двумя разными объявлениями отдельных языковых конструкций, это скрывало истинную проблему, с которой они столкнулись (см. Оригинальный ответ ниже).

Оригинальный ответ

Если мы очистим имена ваших типов, мы сможем довольно быстро выяснить, что здесь не так.

В TriggerEvent мы переименовываем интерфейс Trigger в ITrigger :

 export interface ITrigger {
    // ..
}

export module Trigger {
    //...
    
    export function Serialise(restoredEvent: ITrigger) {
        //...
    }
}
 

Теперь ActionManager мы можем увидеть, что пошло не так:

 import { Trigger as TriggerBase } from "./TriggerEvent";

export module ActionManager {

    type Trigger = TriggerBase amp; (typeof Trigger); 
    //             ^^^^^^^^^^^
    // ERROR - Cannot use namespace 'TriggerBase' as a type.
    var Trigger = TriggerBase;

    var EventsByID = new Map<UUID, Trigger>();
    var PrepreparedInputs = new Map<UUID, Trigger.Serialised>(); 
    //                                    ^^^^^^^ 
    // ERROR - 'Trigger' only refers to a type, but is being used as a namespace here.
}
 

Вы не можете использовать пространство имен (модуль) в качестве типа. (см. Документы). Раньше, поскольку интерфейс и модуль были названы одинаково, TypeScript предоставлял контекстно-соответствующую опцию — интерфейс. Следовательно, ошибок нет! Несмотря на это, у нас все еще есть исходная ошибка 'Trigger' only refers to a type... , потому что тип — это тип, а не пространство имен (или модуль).

Наконец, ActionManager.Trigger.Serialise(stuff); не работает, потому что триггер не экспортируется из модуля ActionManager.

Решение

Поскольку вы не можете назначить a module для a type , обратитесь к модулю непосредственно в вашем PreparedInputs :

     var PrepreparedInputs = new Map<UUID, TriggerBase.Serialised>(); 
 

И, как и в случае с TriggerEvent , экспортируйте, какие типы / переменные вы хотите использовать вне модуля:

 // The interface was renamed to ITrigger in TriggerEvent.
import { Trigger as TriggerBase, ITrigger } from "./TriggerEvent"; 

export module ActionManager {
    export var Trigger = TriggerBase;

    export var EventsByID = new Map<UUID, ITrigger>();
    export var PrepreparedInputs = new Map<UUID, TriggerBase.Serialised>(); 
}
 

Комментарии:

1. Оффтопическая напыщенная речь: разделение статической функциональности на класс модуля, который называется так же, как и сам класс, не сведет всех с ума. Вам просто это не нравится. Этот шаблон очень похож на то, как C разделяет определения в отдельный файл, и я не вижу, чтобы кто-нибудь жаловался на это. Все по-прежнему находится в том же файле, и F12 в каждом редакторе по-прежнему находит нужную функцию, это просто значительно упрощает навигацию по файлу.

2. Я не знаю о разделении определений c (это что-то вроде частичных классов c #?), Но a module — это совершенно другой тип вещей, чем an interface , что в первую очередь привело к проблеме с запросом (они думали, что включают модуль, но на самом деле это был интерфейс). Я уверен, что есть больше нюансов, чем кто-либо из нас раскрывает.

3. Ничего подобного. В C все определения для классов (сигнатуры переменных, сигнатуры методов, общедоступные и защищенные модификаторы) должны быть указаны в файле .h, а содержимое — в файле .cpp. Это также имело место в C. Мой комментарий был ответом на то, что вы сказали, что это сведет всех с ума, кажется очевидным, что у вас мало опыта работы со многими различными языками программирования, поэтому я рекомендую вам как-нибудь попробовать C . Это отличный язык. Связанный: ошибка компиляции, являющаяся неправильной, — это просто ошибка в компиляторе, объединение интерфейсов находится в их документах, поэтому оно поддерживается. Кроме того, я являюсь первоначальным запросчиком.

4. Очень хорошо, я обновил свой ответ. Я по-прежнему считаю, что лучше всего отличать (с помощью уникальных имен) модули от интерфейсов, поскольку в typescript это совершенно разные вещи, но я был слишком широк в своем утверждении.