Typescriptでブラウザ用ライブラリを作成する時に困ったこと

## はじめに
Typescriptがスタンダードになりつつある流れを感じ、勉強がてら既存のライブラリを書き換えようと思ったら、なかなか苦労した話です。

## 困ったこと

既存のライブラリは、以下のような使い方をします。

```use.js
(function(){
    // ライブラリの処理 
    // この中でwindow.Libraryを定義
})();
var setting = {
    text: "text",
    num: 1
};
window.Wapper = window.Wapper || {
    start: function(setting) { 
        window.Library.start(setting);
    }
};
```

なぜこの様な使い方なのかは、様々な事情があるので、割愛します。

ブラウザのwindowオブジェクトにプロパティを追加したいのですが、typescriptの場合、どのように書けばいいかわからなかったので、色々試してみました。

## バージョン

typescriptのバージョンは、3.5.3です。

## 既存ライブラリの書き方

既存ライブラリは、ES2015で書かれていて、babel+webpackで最終的に1ファイルに出力されます。

```index.js
(() => {
    function init(setting) {
        // init処理
    };

    window.Library = window.Library || {
        start: (setting) => {
            init(setting);
        },
    };
})()
```

## Typescriptで書いてみる

とりあえず、ほぼそのまま貼ってみました。

```index.ts
(() => {
    function init(setting: any) {
        console.log(setting);
    }

    window.Lib = window.Lib || {
        start: (setting: any) => {
            init(setting);
        }
    }
})();
```

以下、エラーで怒られました。

```
Property 'Lib' does not exist on type 'Window'.ts(2339)
```

WindowにLibなんてないとのことです。
そりゃそうなんですが、どうやって解決するのがググりました。

[Typescriptで手っ取り早く外部ライブラリを使う](https://qiita.com/iwamatsu0430/items/e7044348a54c328c0cab)

Interfaceでwindowを定義してやることでエラーが回避できるらしい。

```index.ts
interface Window {
    Lib: any;
}

(() => {
    function init(setting: any) {
        console.log(setting);
    }

    window.Lib = window.Lib || {
        start: (setting: any) => {
            init(setting);
        }
    }
})();
```

一応エラーがでなくなったので、これでコンパイルしてみると、問題なく動きました。

<img width="881" alt="スクリーンショット 2019-07-23 19.55.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55871/2caf19fe-74c7-6f41-2ce2-781dda47ad9a.png">

## webpackとの兼ね合い

一応、上記の方法でwindowオブジェクトにプロパティを追加できたのですが、気になったことが。

webpackには、libraryというオプションがあって、これを使うと同様にwindowオブジェクトにプロパティを追加できます。

```
module.exports = {
    mode: 'production',
    entry: './src/index.ts',
    output: {
      path: `${__dirname}/lib`,
      filename: 'index.js',
      library: "Lib",
      libraryExport: "default",
      libraryTarget: "umd"
    },
    ・・・
```

これも有効にした場合、どのように動くのかやってみました。

結果は、問題なく動いたんですが、たぶん、コードでwindowに追加されているので、webpackの設定は、関係なくなっているような気がします。

そこで、コード内でwindowを指定しないように書き直してみました。

```index.ts
class Lib {
    init(setting: any) {
        console.log(setting);
    }
}

export default class {
    static start = (setting: any) => {
        const lib: Lib = new Lib();
        lib.init(setting);
    }
}
```

こんな感じに書き直して、コンパイルを実行。
これも問題なく動きました。

<img width="882" alt="スクリーンショット 2019-07-23 20.10.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55871/e7935979-380f-c20f-6203-7062e107276d.png">


## まとめ

個人的に、コード内には、windowの記述を書かないで、webpackの設定でプロパティを追加する方法の方がスマートなのかなと思いました。

他にも方法があるのかもしれないのですが、一旦、やりたいことはできたので、よかったです。

後、引数を`any`にしていますが、実際にはちゃんと型書いてます。`any`は使わないようにしてます。

## おまけ

webpackのlibrary設定ですが、コード内に`export defalt`の記述があるクラスが存在しないとうまく動かなかったです。

```
class Lib {
    init(setting: any) {
        console.log(setting);
    }
}

export class Main {
    static start = (setting: any) => {
        const lib: Lib = new Lib();
        lib.init(setting);
    }
}
```

<img width="882" alt="スクリーンショット 2019-07-23 20.16.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55871/26a75ea8-1482-c326-475f-7c02fb79ffb7.png">

以上です。