Vue.js+TypeScriptのコンポーネントのクラス定義ミスでハマった話

## 概要

```export default class Hoge extends Vue {}``` のクラス定義をミスった際に不思議な挙動をしたので、覚書。

## 教訓

Vueファイルをコピーしたらクラス名を変更し忘れないようにしよう。


GitHubに利用したプロジェクトをUPしています。実際に試してみたい方どうぞ^^
https://github.com/kai-kou/vue-js-typescript-component-class-name

## 準備

ここではDockerを利用して環境構築していますが、ローカルで構築してもらってもOKです。

```sh
> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> vi Dockerfile
> vi docker-compose.yml
```

```txt:Dockerfile
FROM node:10.8.0-stretch

RUN npm install --global @vue/cli

WORKDIR /projects
```

```yaml:docker-compose.yml
version: '3'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - ".:/projects"
    tty: true
```

```sh
> docker-compose up -d
> docker-compose exec app bash
```

```sh:コンテナ内
> vue create app

Vue CLI v3.0.1
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Linter, Unit
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript for auto-detected polyfills? Yes
? Pick a linter / formatter config: TSLint
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Mocha
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn
  Use NPM
```

```sh:コンテナ内
> cd app
```

これで環境が整いました。


## 検証

新しくコンポーネントを追加します。

```sh
> touch src/components/Hoge.vue
```

```vue:src/components/Hoge.vue
<template>
  <div>hoge</div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Hoge extends Vue {}
</script>
```

追加したコンポーネントをコピーしてもうひとつ追加します。

```sh
> cp src/components/Hoge.vue src/components/Hoge2.vue
```

あえて、```export default class Hoge extends Vue {}``` の```Hoge``` をそのままにしておきます。

```vue:src/components/Hoge2.vue
<template>
  <div>hoge2</div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Hoge extends Vue {}
</script>
```

もとからあるApp.vueで追加したコンポーネントを利用します。

```vue:src/App.vue
<template>
  <div id="app">
    <hoge/>
    <hoge2/>
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from './components/HelloWorld.vue';

import Hoge from './components/Hoge.vue';
import Hoge2 from './components/Hoge2.vue';

@Component({
  components: {
    HelloWorld,
    Hoge,
    Hoge2,
  },
})
export default class App extends Vue {}
</script>
(略)

```

ブラウザで確認してみます。

```sh
> yarn serve
yarn run v1.9.2
$ vue-cli-service serve
 INFO  Starting development server...
Starting type checking and linting service...
Using 1 worker with 2048MB memory limit
 98% after emitting CopyPlugin

 DONE  Compiled successfully in 67085ms                                                                  7:53:09 AM

No type errors found
No lint errors found
Version: typescript 3.0.3, tslint 5.11.0
Time: 28863ms

  App running at:
  - Local:   http://localhost:8080/

  It seems you are running Vue CLI inside a container.
  Access the dev server via http://localhost:<your container's external mapped port>/

  Note that the development build is not optimized.
```

<img width="1243" alt="スクリーンショット 2018-09-26 16.54.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/71912651-5f04-a819-17c7-c97bc076940a.png">

はい。
追加コンポーネントが表示されました。
追加した2つのコンポーネントのClass名は```Hoge``` で同じなのですが、特にエラー出力はありません。

単体テストを追加してみます。

```sh
> touch tests/unit/App.spec.ts
```

```ts:tests/unit/App.spec.ts
import { expect } from 'chai';
import { shallowMount } from '@vue/test-utils';

import App from '@/App.vue';
import HelloWorld from '@/components/HelloWorld.vue';
import Hoge from '@/components/Hoge.vue';
import Hoge2 from '@/components/Hoge2.vue';

describe('App.vue', () => {
  it('Hogeコンポーネントが表示されるか', () => {
    const wrapper = shallowMount(App, {});
    wrapper.is(App);
    expect(wrapper.findAll(HelloWorld)).to.length(1);
    expect(wrapper.findAll(Hoge)).to.length(1);
    expect(wrapper.findAll(Hoge2)).to.length(1);
  });
});

```

テスト実行してみます。

```sh
> yarn test:unit
 WEBPACK  Compiled successfully in 1101ms

 MOCHA  Testing...

  App.vue
    1) Hogeコンポーネントが表示されるか

  0 passing (1s)
  1 failing

  1) App.vue
       Hogeコンポーネントが表示されるか:

      AssertionError: expected { Object () } to have a length of 1 but got 2
      + expected - actual

      -2
      +1

      at Context.<anonymous> (dist/webpack:/tests/unit/Hoge.spec.ts:15:1)

 MOCHA  Tests completed with 1 failure(s)
```

oh。
Hogeコンポーネントが2つとみなされました。
そりゃあ、そうクラス定義しているので、そうですね。
けど、単体テストを書かないと気が付かずにそのまま。。。になりかねないので、 皆さま、うっかりミスには気をつけましょう^^


## 参考

Vue.js+TypeScriptで開発するときの参考記事まとめ
https://qiita.com/kai_kou/items/19b494a41023d84bacc7