前提
既存のWebシステムがあり,工数の都合やシステムの制約からHTML(JSPやThymeleaf)はそのままで,JavaScriptだけTypeScriptに置き換えたいことがあると思います.
既存のWebシステムとなるとjQueryがよく利用されていると思いますので,本投稿ではWebアプリケーションでよくある操作をTypeScriptに書き換えることを目的としています.
ReactやVueなどは使いません.
環境構築+初期設定
環境構築は本題とはあまり関係ないので,こういう環境で実施しましたという紹介だけです.
すでに環境はできている方が多いと思いますので読み飛ばしてください.
まずは必要なパッケージのインストールです.
# 初期設定
npm init #Enter連打
# 必要パッケージのインストール
npm install -D typescript jquery @types/jquery
# TypeScriptの初期設定
npx tsc --init
動作確認のためのサンプルページを作成します.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>テストページ</title>
</head>
<body>
<script
src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
crossorigin="anonymous"
></script>
<script src="app.js"></script>
</body>
</html>
$(function () {
$(document.body).html("hello");
});
サンプルページが作成できたらTypeScriptをコンパイルします.
npx tsc app.ts
app.jsが生成されたら,index.htmlをブラウザで開き,hello
が表示されていれば準備完了です.
各種置き換えについて
ここからは置き換え方法について記載していきます.
変数,定数,関数を定義したい
よくチュートリアルで出てくる単純な足し算です.
var a = 10;
var b = 40;
function sum(a, b) {
return a + b;
}
console.log(sum(a, b));
これをTypeScriptに書き換えると以下になります.
let a = 10;
let b = 40;
function sum(a: number, b: number): number {
return a + b;
}
console.log(sum(a, b));
ぱっと見て:number
という型の宣言が増えています.
これがあることで,sumを数値以外で使おうとしたらコンパイルエラーになったり,returnを忘れているとコンパイルエラーになったりと実行前から色々チェックしてくれます.
変数についてはlet
を利用していますが,定数の場合はconst
を利用してください.
要素を取得したい
このセクションはjQueryを使う場合はJavaScriptもTypeScriptも同じです.
テキスト
<input id="text" type="text" value="ねこ" />
console.log($('#text').val()); // ねこ
// jQuery
console.log($('#text').val());
// non jQuery
console.log((document.getElementById('text') as HTMLInputElement)?.value);
jQueryを使っている場合は元コードと変わりません.
jQueryを使わない場合はHTMLInputElement型へのアサーションが必要になります.
チェックボックス
<input type="checkbox" name="check" value="ねこ" />ねこ
<input type="checkbox" name="check" value="かわいい" />かわいい
<input type="checkbox" name="check" value="にゃー" />にゃー
$('input[name=check]').each(function () {
// 値の取得
console.log($(this).val()); // ねこ
// チェック状態の取得
console.log($(this).prop('checked')); // true
});
// jQuery + アロー演算子
$('input[name=check]').each((_index, element) => {
// 値の取得
console.log($(element).val());
// チェック状態の取得
console.log($(element).prop('checked'));
});
// non jQuery
let checklist = document.getElementsByName('check');
checklist.forEach((element) => {
let input = element as HTMLInputElement;
// 値の取得
console.log(input.checked);
// チェック状態の取得
console.log(input.value);
});
宗教上の理由から(モダンなコードを書いている感を醸し出すために)アロー演算子を使って書きましたが,アロー演算子を使用しない場合は,元のJavaScriptと同じ書き方です.
jQueryを使わない場合はいつものように(?)HTMLInputElement型へのアサーションが必要になります.
セレクトボックス
<select id="selectBox" name="select">
<option value="1">いぬ</option>
<option value="2">さる</option>
<option value="3">きじ</option>
</select>
console.log($('#selectBox').val()); // 値の取得 例:1
console.log($('#selectBox option:selected').text()); // テキストの取得 例:いぬ
// jQuery
console.log($('#selectBox').val());
console.log($('#selectBox option:selected').text());
// non jQuery
let select = document.getElementById('selectBox') as HTMLSelectElement;
console.log(select.value);
console.log(select.options[select.selectedIndex].text);
jQueryはTypeScriptでもそのままです.
jQueryを使わない場合はHTMLSelectElement型へのアサーションが必要です.
イベントを実装したい
ボタンをクリックしたら,チェックボックスにチェックを入れたら,Windowをリサイズしたらなどの条件でイベントを登録している場合の書き方です.
<button id="testButton" value="1">ボタン</button>
$('#testButton').on('click', function () {
console.log(this.value);
});
// jQuery
$('#testButton').on('click', function () {
console.log((this as HTMLButtonElement).value);
});
// non jQuery
document.getElementById('testButton')?.addEventListener('click', function () {
console.log((this as HTMLButtonElement).value);
});
作成したボタンのvalueを取得するためにはHTMLElement型のthisをHTMLButtonElement型にアサーション(キャスト)する必要があります.
※ 作成したい要素に応じて型を変更してください
脊髄反射でfunction
ではなくアロー演算子()=>
を使った場合は,thisがglobalThisになるため,値が取れなくなります.(コンパイルエラーになります.)
クリックした要素を参照する必要がなければアロー演算子でも問題ありません.
宗教上の理由でアロー演算子を使用しないといけない場合は,以下のように記述できます.
// jQuery
$('#testButton').on('click', (event: JQuery.ClickEvent) => {
console.log(event.target.value); // ※targetはany型
});
// non jQuery
document.getElementById('testButton')?.addEventListener('click', (event: MouseEvent) => {
console.log((event.target as HTMLButtonElement).value);
})
親画面の関数を実行したい
※ サーバ上に配置しないと動作しません.
// 親画面
function setUser(userName){
console.log(userName);
}
// 子画面
window.opener.setUser('たま');
// 親画面
function setUser(userName: string) {
console.log(userName);
}
// 子画面
window.opener?.setUser('たま');
ほとんど変わっていません.ほとんどそのまま利用できますが,注意点が2つあります.
- openerはany型であるため,setUserという関数が存在するか不明で引数のチェックなども放棄される
- webpackなどのモジュールバンドラを使うと親画面のsetUserがクロージャの中に閉じ込められるので,子画面から参照できない
webpackを使っていても親画面の関数を実行したい
webpackの説明については割愛します.
// 親画面
interface ParentWindow extends Window{
setUser(userName: string): void;
}
(window as ParentWindow & typeof globalThis).setUser = (userName: string) => {
console.log(userName);
};
// 子画面
(window.opener as ParentWindow)?.setUser('たま');
すでに定義されているWindowを拡張してsetUserを追加します.
そのあとで親画面でsetUserを実装します.
子画面から呼び出すときはopenerがany型なのでWindow型にアサーションしてからsetUserを呼び出します.
これで親画面での関数定義と子画面での呼び出しで型チェックできるようになります.
ただし,親画面でのsetUserを定義し忘れることは検知できません.
※ tsconfigのlibで["DOM"]
を定義しているか,モジュール形式かどうかで定義方法が変わってきます.すべて解説できるほど知識がないのでここまでとしておきます.
まとめ
間違いなどあればご指摘いただけると幸いです.
jQuery使っていると型変換しなくても良いので楽