SE(たぶん)の雑感記

一応SEやっている筆者の思ったことを書き連ねます。会計学もやってたので、両方を生かした記事を書きたいと考えています。 でもテーマが定まってない感がすごい。

JavaScriptやったことない人間がTypeScriptに初挑戦した

最近の開発で、Webアプリのプロトタイプを作る仕事がありました。今も続けていますが。

その中で、ほとんどWebのフロントエンドをやったことがない人間(筆者)が、TypeScriptを使ってみたので、その感想と紹介をしてみます。

認識違いはありそうなので、誤り等はご容赦ください。

筆者経験(担当時点)

  • バックエンド(サーバーサイド)は経験あり。Web APIを作れと言われたら作れる
  • フロントエンドは、ブラウザはほぼ経験なし。WPFはかなりいじっているし、UWPに関してもそれなりに知っている
  • いわゆるMVCフレームワークに基づいたWebアプリの仕組みについては理解している
    既存のフレームワークだけだと、動的ページを作るのが難しいことは知っている、ということ

経緯

条件を複数指定して、検索結果を表示する画面を作る必要がありました。見た目はbootstrap4で繕っています。

単純なものであれば、固定入力欄を用意して、フォームでリクエスト飛ばして、ページ遷移させて結果を表示するという形で実現できます。一度それで作りました。

しかし、条件自体を追加しながら検索したい、という要望が発生する*1のは容易に想像できるため、その機能を前提とした作りに挑戦しよう、と思いました。

そこで、

  • 検索条件をJSONに詰め込んで、リクエストとしてサーバーに送る
  • 検索結果をレスポンスで送り返す
  • そのままページ遷移させずに表示

という作りに挑戦してみました。

クソ下手な絵(ペイント3D作)

f:id:hiroronn:20181103152157p:plain

…慣れた人にとっては大したことはないんですよねきっと。

仕組み上、どう考えてもJavaScriptを使う必要がありますが、あまり良い印象を持っていないため、TypeScriptで挑戦してみよう、というのが、今回使った経緯です。

余談ですが、バックエンドはDjangoで、Django-Rest-Frameworkを使ってWeb APIを作りました。それはそれで苦労しましたが、仕組みそのものは理解していたので、大したことはなかったです。
このときに、検索処理をフォームからでもREST API経由でも使えるように、Repository等を利用して処理を一つにしたのは、まあいいでしょう。

TypeScript使用準備

開発にはVisual Studio Codeを使用しています。

が、そもそもTypeScriptで作ったスクリプトを動かすには、JavaScriptに変換(トランスパイル)する必要があります。

typeScriptコンパイラを簡単に使うには、まずNode.jsをインストールし、一緒に入ってくるnpmを使って

npm install -g typescript

とすればよいです。*2

tsconfig.json作成

コンパイル自体は、上記のようにインストール後に

tsc index.ts

のようにファイルを指定すると行えます。

ただ、実際に開発する場合は、生成後のJavaScriptファイルを置く場所を変更したり、TypeScript自体をデバッグできるようにしたり、フォルダ内にあるスクリプトを一括でJavaScriptに変更したい等あります。

そのときは、TypeScriptの開発フォルダで

tsc --init

と入力し、tsconfig.jsonを生成します。

私が有効にした設定は、

{
  "compilerOptions": {
    //途中省略
    "declaration": true,
    "sourceMap": true,
    "outDir": "",  //実際にはJavaScriptの出力先を指定
    //以下省略
  }
}

という感じです。これをやった後は、tsconfig.jsonがあるフォルダでtscコマンドを実行すると、設定したとおりにJavaScriptが生成されます。*3

単にWebページから呼び出すだけなら、この程度の環境設定で使えるようになります。

TypeScriptのコーディング

これは、ここ良かったなぁ、と思ったところを挙げていきます。

型ヘルプ(Intellisense)が充実している

これは非常に大きかったです。

例えば、JavaScript

const btn = document.getElementById('search')

という処理を書くと、見た目上、btnHTMLElementとして扱われますが、実際は要素に応じた型です。そして、開発者にとっては、IDを指定した時点で、それが何なのかわかっています。

そこで、TypeScriptでは、

const btn = <HTMLButtonElement>document.getElementById('search');

のように書くことでキャストされ、ソース上要素型を明示できます。

他にも、動的に要素を作成する場合、

values.forEach(v => {
    const item = document.createElement("a");
    item.classList.add("list-group-item", "list-group-item-action", "list-group-item-primary");
    item.href = v.url.href;
    item.text = v.name;
    item.target = "_blank";
    resultElem.appendChild(item);
});

というソースがある場合に、

f:id:hiroronn:20181103161629p:plain

というように、作っているのはAnchor要素だよ、というのを判断して出してくれます*4

筆者のようなJavaScript初心者でも、どの型にどのプロパティがあるのかはっきりわかることで、

  • ミスを減らせる
  • 使うべきプロパティが一覧化されるため、名前を調べなくてよい(要素名を知っている前提)
  • 勘違いを減らせる

というような効果が見込めます。

まあ、document.createElementだと、JavaScriptであっても型解釈ぐらいはやってくれますが…getElementByIdでは恩恵にあずかれます。

慣れ親しんだクラスが使える

これは、C#JavaPython等に慣れ親しんだ人にとっては、大きすぎる利点でしょう。

JavaScriptでは、prototypeというものを使ってクラスを表現*5していましたが、これが個人的には取っつき辛い部分でした。

TypeScriptでは、最初からクラス構文が用意されているので、大変分かりやすいです。

当然、メソッドの引数はanyではなく型指定されるので安心です。

たとえば、TypeScriptのクラスとメソッド

class SearchChanger {
    public Show(samples: Array<Sample>) {
        //処理
    }
}

というものがあると、JavaScriptでは

var SearchChanger = /** @class */ (function () {
    SearchChanger.prototype.Show = function (samples) {
        //処理
    }
}

という形に変換されます。正直、JavaScriptのほうのメソッドを手で書くように言われたら、頭がついていきません。functionがたくさん出てきて読みづらいですし。

引数のsamplesは表示対象オブジェクトのリストですが、JavaScriptではanyです。TypeScriptなら型情報を出しながら、しかも誤った要素を参照する危険性を減らしながら、安全にコーディングできます。

このおかげで、既存の知識を活用しながら、素早くコーディングできました。

躓いた点

TypeScriptは便利でしたが、躓いた点もあります。

thisの扱い

TypeScriptでは、クラス内のプロパティをthis.プロパティみたいな形で参照できます。

ただ、クラス内でHTML要素のイベントを作成し、登録した際に、そのイベント内のthisがクラスではないものが入ってきました。

ソース例はこんな感じです。(TypeScript)

    constructor(combo1: HTMLSelectElement, combo2: HTMLSelectElement) {
        this.parentCombo = combo1;
        this.childCombo = combo2;
        this.index = this.parentCombo.selectedIndex;
        this.parentCombo.addEventListener("change", this.parentChanged);
    }

このthis.parentChanged内がおかしくなっていました。

解決策として、イベントのメソッドをアロー式にして、プロパティとして宣言すればいいというのを見つけ、そしたらうまくいきました。下記のような感じです。

    parentChanged = () => {
        //処理
    }

正直よくわかりません。きっとJavaScriptをよく知る必要があるのでしょう。

いくら便利にコーディングできるとはいえ、JavaScriptを無視できるわけではない点は、肝に銘じたほうが良いと感じています。

おわりに

あくまで感想、という内容になります。

動的ページを作るハードルは、もっと高いと思っていましたが、上に書いた検索ページ自体はWeb APIの準備やルーティング等、バックエンドの作業含めて一日で作成できました*6

TypeScriptは、取っつきやすい、という利点があるように思っています。JavaScriptのカオスさを軽減している感じです。

しかし、はまりポイントがあるようですし、JavaScript自体の学習も必要だと思いました。

フロントエンドも楽しいです。すごく勉強になりました。

*1:それが業務上必要かどうか引き出すために作っている

*2:これでローカルのグローバル環境にインストールされる

*3:Visual Studio Codeのタスクに登録するなど、便利に扱えるようになる

*4:ちなみに、resultElemはDiv要素

*5:ES6からclass構文追加。ここでは、その前までの話となる

*6:全部一人で作っている状態