AngularでInterface指定でDIしたかったのでChatGPTに聞いてみた

Angular＋SpringBootでWebアプリを実装することが多く、どちらもDIコンテナを備えてますが、
・Angular -> 実クラスを指定してDI
・SpringBoot -> Interfaceを指定してDI
として通常実装しています。
ただ、依存性の注入。という意味では利用側はInterfaceだけを意識して、実装クラスの解決をDIコンテナに任せたく、AngularでもInterfaceを指定してDIができないものかと思っていました。

Angular界隈では常識なのかもしれませんが、私は知らなかったのでChatGPTに聞いてみました。
（今更流行りにのって。。。）

## ChatGPTとの会話
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/90b48ab2-a732-8037-63f9-a103f912dfb1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/870a7e4e-a5c5-67c5-994c-60e8075fd927.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/c2425231-f61e-230f-bbc2-c97c9570ee35.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/2184bc95-7152-d86a-8784-cd0319e13149.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/4e64e881-5cd1-fcf8-b9aa-220cfc03666d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/5adb0071-2145-6ac6-3479-a9b35184a4b7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/2ceb2eb7-003e-73ac-5854-8ced8515b900.png)

### いやこいつ天才か！？
すごすぎるだろ。。。知りたかったことが2，3分で知れました。。。
ただし、ChatGPTは嘘を教えることもあるので、実際にやってみます

## 実装

### インタフェース
```typescript:service.interface.ts
export interface ServiceInterface {
  show: () => void;
}
```

### 実装クラス(prod)
```typescript:prod.service.ts
import { Injectable } from '@angular/core';
import { ServiceInterface } from './service.interface';

@Injectable({
  providedIn: 'root',
})
export class ProdService implements ServiceInterface {
  constructor() {}

  show() {
    console.log('This is ProdService.');
  }
}
```

### 実装クラス(test)
```typescript:test.service.ts
import { Injectable } from '@angular/core';
import { ServiceInterface } from './service.interface';

@Injectable({
  providedIn: 'root',
})
export class TestService implements ServiceInterface {
  constructor() {}

  show() {
    console.log('This is TestService.');
  }
}
```

よし、これであとはapp.module.tsのprovidersにfactoryを設定して、利用クラス側でDIすればOK。
prodで動かした場合はprod.service.ts、それ以外はtest.service.tsがDIされて動く想定。

ん、、、、エラー出てるやんけ。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/75894c0a-d364-5daa-49c9-7b24fe3900ab.png)

provideにinterfaceは指定できないな。。。ChatGPTに嘘つかれたか。。。
調べてみた感じやり口としては二通りありそう。
１．Interfaceを継承するabstract classを実装して、abstract classをprovideに指定する
２．Interfaceに紐づくInjectionTokenを生成し、それをprovideに指定する

好みですが、２の方法で対処してみます。

```typescript:app.module.ts
import { environment } from 'src/environments/environment';
import { ProdService } from './service/prod.service';
import { TestService } from './service/test.service';
import { ServiceInterface } from './service/service.interface';

// environmentでインスタンス化するクラスを変えていく
const myFactory = () => {
  return environment.production ? new ProdService() : new TestService();
};

// ServiceInterfaceに紐づくInjectionTokenを生成する
export const SERVICE_TOKEN = new InjectionToken<ServiceInterface>(
  'service.interface'
);

@NgModule({
  declarations: [AppComponent, CustomTooltipComponent, CustomTooltipDirective],
  imports: [BrowserModule, AppRoutingModule, OverlayModule],
  providers: [
    {
      // 生成したInjectionTokenをprovideに指定する
      provide: SERVICE_TOKEN,
      useFactory: myFactory,
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}
```

あとは利用クラスを書くのみ
```typescript:app.component.ts
import { Component, Inject } from '@angular/core';
import { SERVICE_TOKEN } from './app.module';
import { ServiceInterface } from './service/service.interface';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  title = 'angular-sample';
  // DIする際に@InjectでInjectionTokenを指定する
  constructor(@Inject(SERVICE_TOKEN) sv: ServiceInterface) {
    sv.show();
  }
}
```

`@Inject`でInjectionToken指定するのが、個人的には微妙ですね。。。
なんとかならんのかな。その場合はabstract class方式で実装するのが良いのでしょうか。

## いざ、稼働確認
```
ng serve --configutaion production
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/4eec87fc-4177-11df-d3f5-4dd9dc8dfba1.png)

```
ng serve
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38911/5c185555-7937-3f24-4a05-721d24ad87c4.png)


おおおおおおおおおおおおおおおおおおおお、すげええええええ！！
紆余曲折ありましたが、やりたいことはできました。ChatGPTすごい！！！

使ってみて思いましたが、AIに仕事奪われるとか、もうおれらの価値なんて、、、、ってネガティブな感じではなく、ChatGPTを利用しまくってもっともっと成長するぜって気になりました。
とにかく、情報の収集効率がほんとに良くて、案Aと案Bどっちが良いかな？みたいな相談にも使えたりするのでChatGPTからの回答自体が非常に勉強になることが多いです。

ただ、今回みたいにガセ情報もあるので、もう一段踏み込んで考えたり、ググって裏どりするのが重要ですね。
また、相談系もこちらの誘導尋問的なこともできるし、ChatGPTが忖度した回答してくるときもあるので、案Aと案Bのメリットデメリットを聞くとか、公平性を意識した質問が良いんですかね。

以上です。