Красивое исправление проблемы циклических зависимостей в Javascript / Typescript

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

Минимальный проект для воспроизведения проблемы можно найти здесь. Вот еще более простой обзор:

Статья:

import { ManyToMany } from './ManyToMany';
import { Tag } from './Tag';

export class Article {
    @ManyToMany(Tag)
    tags: Tag[] = [];
}

ManyToMany:

//no imports
export function ManyToMany(entity) {
    …
}

Тег:

import { ManyToMany } from './ManyToMany';
import { Article } from './Article';

export class Tag {
    @ManyToMany(Article)
    articles: Article[] = [];
}

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

Я нашел список обсуждений, но ни одно из них не выглядит элегантным:

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

Есть ли лучшее, более элегантное решение без создания дополнительных файлов или перемещения кода?


person Ionel Lupu    schedule 20.10.2019    source источник
comment
Как вы используете этот импорт?   -  person Jonas Wilms    schedule 20.10.2019
comment
Проблема в том, что вашему декоратору ManyToMany передается значение, сам объект класса. Он понадобится вам, чтобы принять тип, но я понятия не имею, как это может быть возможно.   -  person Bergi    schedule 20.10.2019
comment
Или, может быть, вам вообще не следует заставлять его принимать аргумент, а лучше заставлять его анализировать объявление типа свойства, для которого он был вызван? К сожалению, я понятия не имею, возможно ли это в TypeScript, но это было бы идеальным решением.   -  person Bergi    schedule 20.10.2019
comment
Решение, которое, безусловно, будет работать, - сделать ссылку ленивой, то есть передать функцию, возвращающую объект, и не оценивать ее сразу (во время оформления свойства), но позже, когда все модули загружены, классы инициализируются, и первый экземпляр в процессе создания: @ManyToMany(() => Tag)   -  person Bergi    schedule 20.10.2019
comment
Я пробовал отложенную загрузку, но она не работает https://stackoverflow.com/questions/63953101/typescript-circular-dependency-on-decorator   -  person tinytanks    schedule 19.09.2020


Ответы (1)


Согласно комментарию Берги, решение, которое будет работать, - сделать ссылку ленивой, то есть передать функцию, возвращающую объект, и не оценивать ее сразу (во время оформления свойства), но позже, когда все модули загружены, классы инициализированы и создается первый экземпляр.

Я попытался реализовать тот же https://stackblitz.com/edit/typescript-f5rm9u здесь. Пожалуйста, проверьте реализацию.

ManyToMany

export function ManyToMany(entity=() => entity) {
    return function(target, name)  {
        console.log(entity.name);
    };
}

Ответ на кредит: @Bergi

person Prashant Biradar    schedule 20.10.2019
comment
Нет, Tag => Tag так же бессмысленно, как x => x. Ваша реализация вообще не регистрирует никаких имен. - person Bergi; 20.10.2019
comment
Я использовал этот ответ и ваш ответ @Bergi, а также добавил setTimeout () внутри ManyToMany, и он работает. Единственная проблема в том, что код теперь асинхронный. - person Ionel Lupu; 20.10.2019
comment
Нет необходимости делать его асинхронным, достаточно отложить его до тех пор, пока экземпляр не будет создан (или все, что должен создать ваш декоратор, будет выполнено) - person Bergi; 21.10.2019
comment
@Bergi, проверьте, stackblitz.com/edit/typescript-f5rm9u здесь. вы можете увидеть журнал для статьи с 0 тегами. Кроме того, мы можем создать экземпляр тега, поэтому здесь мы используем стрелку x = ›x или tag =› tag означает, что он относится к текущему вызывающему экземпляру. - person Prashant Biradar; 21.10.2019
comment
Вы регистрируете имя функции стрелки (параметр entity), которая является пустой строкой, и имя свойства, к которому был применен декоратор (параметр name). - person Bergi; 21.10.2019
comment
да. Спасибо, @Bergi, мне плохо. Я обновил ManytoMany как export function ManyToMany(entity=() => entity). который регистрирует объект статьи и тега. - person Prashant Biradar; 21.10.2019
comment
В конце концов, я пришел с этим решением: stackblitz.com/edit/typescript-rhmdhq ? file = index.ts. Это то, что я ожидал увидеть в консоли - person Ionel Lupu; 21.10.2019
comment
У меня была похожая проблема, но я не нашел решения: stackoverflow.com/questions/63953101/ - person tinytanks; 19.09.2020