逆引きマニュアル: JavaScriptからTypeScriptへの変換方法(コンパイルを通すまで)

投稿日:

やりたいこと

JavaScriptで書かれたコードをTypeScript化する方法です。

前提条件

WebStorm 2018.2で確認しています。

概要

まずはコンパイルできるようにすることが最優先です。 それから、TypeScriptらしく変えていくといいと思います。

手順

次に、tsconfig.jsonを作成します。 WebStormではデフォルトで以下のように作成されます。 自分が使っているのはGoogle Chrome拡張なので、 とりあえずes2017にしています(2018でもいいかも)。

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "sourceMap": true
  },
  "exclude": [
    "node_modules"
  ]
}

(補足)後から知りましたが、allowJs(最後のSは小文字)を使うと、混在した環境でも問題ないみたいです。

次に、拡張子を.jsから.tsに変えます。 WebStormではRefactor→Renameを選択します。 リファクタリングできないという警告が出ますが、Yesを押して進めます。

Compile TypeScript to JavaScript?と聞かれるのでYesを選択します。 それからエラーを潰していきます。

TS2304: Cannot find name ‘xxx’

変数が見つからないときに発生します。 *.jsからは検索しないようなので、他のファイルを*.tsに変更してみます。

TS2339: Property ‘xxx’ does not exist on type ‘yyy’

型の違いによるエラー

例えば以下のコードでエラーが出ます。 エラーメッセージはProperty 'value' does not exist on type 'HTMLElement'

const exportTextArea = document.getElementById('exportTextArea');

exportTextArea.value = lines.join("\n") + "\n";

これは、document.getElementById('exportTextArea')で取得できるのが HTMLTextAreaElementなのに対し、 TypeScriptはHTMLElementとしか解決できないために発生したエラーです。 (HTMLで定義されているので分からないですよね)

こういうときはまず、asを使ってキャストを行います。 (コンパイルを通すのが最優先)

const exportTextArea = document.getElementById('exportTextArea') as HTMLTextAreaElement;

インスタンス変数未定義によるエラー

例えば以下のようなコードです。

class BlockedSite {
  constructor(item) {
    this.url = item.url; // ここでエラーになる
  }
}

こういうときは、明示的にインスタンス変数を定義します。

class BlockedSite {
  url: string;

  constructor(item) {
    this.url = item.url; // ここでエラーになる
  }
}

handlerの場合はややこしいので、一旦anyにしておきます。

JSONによるエラー

JSONの値を参照するときにコンパイルエラーになります。

const BlockedSitesRepository = {
    loadData: async function () {
        const items = await ChromeStorage.get({blocked: []});
        ...
        for (const item of items.blocked) { // ここでエラー
            ...
        }
    }
    ...
}

こういうときは、interfaceを定義します。 anyは手抜きです

interface BlockedSitesList {
    blocked: any[];
}

const BlockedSitesRepository = {
    loadData: async function () {
        const items = await ChromeStorage.get({blocked: []}) as BlockedSitesList;
    }
    ...
}

TS2451: Cannot redeclare block-scoped variable ‘xxx’

組み込みの変数とかぶっている場合に発生します。 自分はStorageという変数名がエラーになりました。 おそらく、Web Storage APIのStorageインタフェースと 競合していたと思われます。

Storage → ChromeStorageに変更しました。

TS2554: Expected 1 arguments, but got 0.

引数の数が合わない場合です。 例えば以下のようにハンドラとしても使う関数に対し、 使わないパラメータをignoreとしてたのですが、これがまずかったようです。

this.mediator.editUrl()

// 定義
editUrl(ignore) {
}

これは定義側を直すのがいいと思います。

補足

マニュアル