JavaScriptで書いているWebPackのプロジェクトをTypeScriptに移行・書き換えした

背景
====

学習がてら開発しているクライアントサイドのみの小さなSPAをJavaScriptで書いていたが、変数に入っているのが何のオブジェクトなのかがわからなくなって混乱してきたので重い腰を上げてTypeScriptに移行することに。

環境
====

Vue.jsを使ったJavaScript（ES6）のコードをWebPackで固めてクライアントサイドで実行している。
エディタはVSCode

typescriptとts-loaderモジュールのインストール
====

WebPackや各種モジュールを使うのにすでにnpmやNode.jsやWebPackはインストール済みなので、TypeScriptとそれをWebPackにロードするためのローダーのモジュールをnpmでインストールする。

```
$ npm i typescript ts-loader
```

webpack.config.jsの書き換え
===

既存のwebpack.config.jsファイルのmodule.rulesに.tsファイルのロードと解決に関する設定を追加したところ以下のようになった。

```javascript:webpack.config.js
module.exports = {
    mode: 'development',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    "style-loader",
                    "css-loader"
                ]
            },
            { // .tsファイルをロードする設定を追加
                test: /\.ts$/,
                use: 'ts-loader'
            }
        ]
    },
    resolve: {
        alias: {
            'vue$' : 'vue/dist/vue.esm.js'
        },
        // 拡張子.tsのファイルをjsに変換
        // （この設定はファイル名の解決に使われるらしい。詳細を後述）
        extensions: ['.ts','.js']
    },
    entry: './src/index.js',
    output: {
        path: `${__dirname}/public`,
        filename: 'main.js'
    },
    devServer: {
        contentBase: './public'
    }
}
```

ts設定ファイルの追加
====

以下のようなtsconfig.jsonを追加しました。詳細は後述。

```javascript:tsconfig.json

{
    "compilerOptions": {
        "target": "esnext",
        "module": "commonjs",
        // "noImplicitAny": true, // 今まで書いたJSのコードで定義した変数がとりあえずそのまま使えるように
        "esModuleInterop": true,
    },
}
```

拡張子とソースコードの変更
====

JavaScriptで書かれたコードはそのままTypeScriptのコードとして通用すると言うことなので、以下のようなprogram.jsをprogram.tsにリネーム。

`id`や`number`のような仮引数に対して"Parameter 'id' implicitly has an 'any' type.ts(7006)
"と怒られた。tsconfig.jsonのnoImplicitAny : trueをコメントアウトすることで解消。

```javascript:program.js
 export default class Program {
    constructor(id, number, title, startTime, lengthMinute, unlockOffsetMinutes) {
        this.id = id;
        this.number = number;
        this.title = title;
        this.startTime = startTime;
        this.lengthMinute = lengthMinute;
        this.unlockOffsetMinutes = unlockOffsetMinutes;
        this.isSelected = false;
    }
    /* 色々な処理が書かれているけど省略 */
}
```

すると、`Property 'id' does not exist on type 'Program'.ts(2339)`と怒られた。TypeScriptのクラス定義ではコンストラクタ外でプロパティの定義が必要らしい。VSCodeのQuick FixでDeclare propertyすることで解消。

```typescript:program.ts
 export default class Program {
     id: any;
     number: any;
     title: any;
     startTime: any;
     lengthMinute: any;
     unlockOffsetMinutes: any;
     isSelected: boolean;
    constructor(id, number,title, startTime, lengthMinute, unlockOffsetMinutes) {
        this.id = id;
        this.number = number;
        this.title = title;
        this.startTime = startTime;
        this.lengthMinute = lengthMinute;
        this.unlockOffsetMinutes = unlockOffsetMinutes;
        this.isSelected = false;
    }
    /* 色々な処理が書かれているけど省略 */
}
```



遭遇したエラー
====

Module not found: Error: Can't resolve './program' in '/Users/rlcl-226/git/gachi-taite-designer-web/src'
----

webpackが出したっぽいエラー.
`./program` と言うファイルを探した結果見つからなかったと言っているように見える。webpack.jsの`resolve.extensions`の設定をすることで解消。
WebPackがprogram.jsを探しに来た時に、TypeScriptコンパイラがprogram.tsをコンパイルしたものを渡してあげるという設定だろうか？

終わりに
====

とりあえずTypeScriptへの移行の最初のファイルはこれで完了した。
これとは別のソースファイルをTypeScriptに移行しようとした時、moment-duration-formatでエラーが出て直したりしたが、それは次の記事で解説する。

↓次の記事
https://qiita.com/a-yonenaga/items/9f8d1a0fcf898e127b26