82779
例えば下記のようなシンプルな循環参照があります。

DepartmentSearchDialogComponent.ts

```

    import UserSearchDialogComponent from './user-search-dialog.component';
    
    export class DepartmentSearchDialogComponent {
        // ...
    }
    
```

UserSearchDialogComponent.ts

```

    import DepartmentSearchDialogComponent from './department-search-dialog.component';
    
    export class UserSearchDialogComponent {
        // ...
    }
    
```

  * 「組織検索ダイアログ」は「ユーザ検索ダイアログ」を呼び出せる  
（組織を検索するための条件に組織長ユーザが指定可能で、それを指定するためにユーザ検索ダイアログを呼び出せる）

  * 「ユーザ検索ダイアログ」は「組織検索ダイアログ」を呼び出せる  
（ユーザを検索するための条件に所属組織が指定可能で、それを指定するために組織検索ダイアログを呼び出せる）

この時、typescriptは `WARNING in Circular dependency detected:` を警告します。

typescriptで以下のようなことを実現しようとする場合、どう書けばよいでしょうか？

  * C言語のincludeガード
  * phpのrequire_once

**環境**

```

    # npm ls -g typescript
    -- @angular/cli@7.3.6
      `-- @schematics/angular@7.3.6
        `-- typescript@3.2.4
    
```

※ typescrptのバージョンが3.2.xであるため、3.8以降で利用できるtypeによる回避はできそうにありません。
CC BY-SA 4.0
2021-09-30T03:22:48.557
82770
2021-09-30T13:58:14.433
2021-09-30T04:24:35.530
3060
25396
question
typescript angular import
typescriptで循環import（WARNING in Circular dependency detected）を解決するには？
循環参照を検知してテストで落とす、といったことをするのは最近だと[dependency-
cruiser](https://github.com/sverweij/dependency-cruiser)が有力でしょうか。

実装自体を改善したいのであれば、循環参照している実装方法はそもそもどこかが間違っている可能性が高いです。  
改善のパターンとしては、UserSearchDialogComponentとDepartmentSearchDialogComponentの共通部分を切り出して互いに依存させる方法と考えられます。いわゆる抽象に依存する方法です（SOLIDの原則）。

Dependency Injection、Mixin、Mediatorパターン、EventEmitterなどを利用して実装するなど方法が考えられます。

色々と書き方はあると思いますが、イメージしやすいように一例として次のようなサンプルを示しておきます。

```

    // types.ts
    export interface BaseComponent {
      // 自分自身のダイアログを開く
      showDialog: () => void;
      // 別のコンポーネントを登録する（外部の依存を取り込む）
      register: (anotherComponent: BaseComponent) => void;
      // 別のcomponentのダイアログを開く
      showAnotherDialog: () => void;
    }
    
```

```

    // department-search-dialog.component.ts
    import type { BaseComponent } from "./types";
    
    export class DepartmentSearchDialogComponent implements BaseComponent {
        private anotherComponents: BaseComponent[] = [];
        constructor() {}
    
        public register(anotherComponent: BaseComponent) {
            this.anotherComponents.push(anotherComponent);
        }
    
        public showAnotherDialog() {
            this.anotherComponents.forEach(anotherComponent => {
                anotherComponent.showDialog();  
            })
        }
    
        public showDialog() {
    
        }
    }
    
```

```

    // user-search-dialog.component
    import type { BaseComponent } from "./types";
    
    export class UserSearchDialogComponent implements BaseComponent {
        private anotherComponents: BaseComponent[] = [];
        constructor() {}
    
        public register(anotherComponent: BaseComponent) {
            this.anotherComponents.push(anotherComponent);
        }
    
        public showAnotherDialog() {
            this.anotherComponents.forEach(anotherComponent => {
                anotherComponent.showDialog();  
            })
        }
    
        public showDialog() {
            // 
        }
    }
    
```

```

    // main.ts
    import { UserSearchDialogComponent } from "./user-search-dialog.component";
    import { DepartmentSearchDialogComponent } from "./department-search-dialog.component"
    
    function main() {
        const userSearchDialogComponent = new UserSearchDialogComponent();
        const departmentSearchDialogComponent = new DepartmentSearchDialogComponent();
    
        userSearchDialogComponent.register(departmentSearchDialogComponent);
        departmentSearchDialogComponent.register(userSearchDialogComponent);
    
        userSearchDialogComponent.showAnotherDialog(); // departmentSearchDialogComponentも開く
        departmentSearchDialogComponent.showAnotherDialog(); // userSearchDialogComponentも開く
    }
    
```

こういう感じで切り出すと循環参照を発生させずに済みます。どういった実装が適切かはケースバイケースなので、現状こたえられるのはこれくらいです。
CC BY-SA 4.0
2021-09-30T13:58:14.433
82779
2021-09-30T13:58:14.433
7997
82770
answer