2016年12月07日

初めてのTypeScript (4) - classを定義し別ファイルから利用してみる


これは TypeScript Advent Calendar 2016 の7日目の記事です。

今度は、複数のファイルに分けての開発に挑戦です。

クラスを定義する

実際の開発になると、複数のファイルに分割して、TypeScriptのコードを書くことになるとおもうんだけど、それってどうやるんだろうか。

試しに、Stopwatchクラスを書いてみます。 スタートとストップの機能だけの単純なものとします。計測のリスタートは考慮しません。

やっぱりC#同様、名前空間を定義しその中にクラスを定義したい。 ということで、定義したのが以下のクラス。

namespace utils {

    export class Stopwatch {

        private startTime: number;
        private stopTime: number;

        static startNew(): Stopwatch {
            let sw = new Stopwatch();
            sw.start();
            return sw;
        }

        start() {
            this.startTime = Date.now();
        }

        stop() {
            this.stopTime = Date.now();
        }

        elapsed(): number {
            return this.stopTime - this.startTime;
        }

        toString(): string {
            let tms = this.elapsed();
            let h = Math.floor(tms / (1000 * 60 * 60));
            let m = Math.floor(tms / (1000 * 60));
            let s = Math.floor(tms / 1000);
            let ms = Math.floor(tms % 1000);
            return this._zeroPadding(h) + ':' + this._zeroPadding(m) + ':' + 
                   this._zeroPadding(s) + ':' + this._zeroPadding(ms, 3);
        }

        private _zeroPadding(n: number, d: number = 2): string {
            let zero = '';
            for (let i = 1; i < d; i++)
                zero += '0';
            return (zero + n).slice(-d);
        }
    }
}

クラスには、publicではなく、exportを付けるようです。

toString()が意外と面倒でした。もっと簡単に書ければいいのにと思います。 javascriptの知識も中途半端なので、こんな、ダサいコードしか書けませんでした。 なんらかのライブラリを使えば、もっとすっきりと書けるのかもしれませんが、今回は、TypeScriptの勉強なので、まあ、これで妥協しておきます。 C#のToString()って、なんて素敵なんだろと改めて思いました。

ついでに、処理結果とかをブラウザで確認できるように、Traceクラスも定義してみました。

    export class Trace {
        static writeLine(s: any): void {
            let el = $('#Trace__');
            if (el[0] === undefined) {
                el = $(document.body).append("<hr><div id='Trace__'></div>");
                el = $('#Trace__');
            }
            el.append(s + "<br>");
        }
    }

この2つのクラスを、utils.ts というファイル名で保存します。

と、ここまで書いて気がついたのですが、namespaceの名前は、PascalCaseで書くのが流儀のようです。camelCaseで書いちゃいました、直すの面倒なので、このままとします。

 

別のソースファイルからクラスを利用する

これを、別のtsファイル(app.ts)から参照してみます。

作成したのは、1からnまでの整数の和を求めるプログラムで、その時間をStopwatchで計測し、 かかった時間と、求めた答えをTraceを使いブラウザに表示しています。

import Stopwatch = utils.Stopwatch;
import Trace = utils.Trace;

namespace ProjectEuler {
    class Problem {
        solve(maxnum: number): number {
            let sum = 0;
            for (let i = 1; i <= maxnum; i++)
                sum += i;
            return sum;
        }
    }

    class Application {
        static run(): void {
            let sw = Stopwatch.startNew();
            try {
                let p = new Problem();
                let n = p.solve(100000);
                Trace.writeLine(n);
            } finally {
                sw.stop();
                Trace.writeLine(sw.toString());
            }
        }
    }

    $(() => {
        $('#solveButton').on('click', () => {
            Application.run();
        });
    });
}

importで、Stopwatchクラスと Traceクラスを名前空間を指定せずに使えるようにしています。

同じディレクトリにあるのならば、C#の using みたいに、

import utils;

だけでOKにできれば、もっと楽なんですけどどね。

ちなみに、HTMLは、こんな感じです。

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"
            integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
            crossorigin="anonymous"></script>
    <script src="utils.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <h3>Sample App</h3>

    <button type="button" id="solveButton">Solve</button>
</body>
</html>

今回は、NuGetでjQueryをインストールするのではなく、CDNのものを参照するようにしています。 あたりまえだけど、HTMLからも utils.js を参照しています。これがないと、動きません。

では実行してみます。 無事、5000050000 と答えを出してくれました。


もうひとつの書き方

いろいろ調べていたら、さきほど書いた書き方とは別の書き方があるようです。

import * as utils from "./utils"

このようにしてインポートできるようです。

でも、これだとビルドエラーになってしまいます。このあたりが、よくわかっていないのですが、utils.ts で namespaceを書いてあるのがダメっぽいです。

namespaceがダメってのはどう考えてもおかしいので、僕の理解が不足しているからだと思うのですが、やり方わからないので、utils.ts から namespaceを取りました。

なお、利用する側の app.ts も書き換えが必要のようです。

StopwatchやTraceを利用するには、utils.Stopwatch.startNew(); のように、utils. で修飾します。

これで、ビルドド通るようになりました。

これでOK、と思ったら、実行時に

Uncaught ReferenceError: exports is not defined
Uncaught ReferenceError: require is not defined

というJavaScriptのエラーが出てしまいます。ビルドして作成される .js ファイルには、確かに、exports というキーワードらしきものが使われています。このexportsが定義されていないってどういうことよ?

調べてみたら、importとexportによるモジュール定義/利用は、今のブラウザでは使うことができないようです。

Babel とか Browserifyとかを使うと、jsファイルをさらに変換し、ブラウザが実行できるコードにしてくれるらしいのですが、どうしたら良いのか...

Webで検索してみても日本語の情報ほとんどないし、なんか僕には理解できない難しい手順を踏まなくてはいけないようで、さっぱりわかりません(T T)

とりあえず、最初に書いた方法ではうまくいわけですから、良しとしましょう。

今日わかったこと、TypeScriptのモジュール絡みはC#プログラマーには難しすぎるということ。


 

  

Posted by gushwell at 22:23Comments(0)TrackBack(0)TypeScript

2016年12月05日

初めてのTypeScript (3) - knockout.jsを利用してみる


これは TypeScript Advent Calendar 2016 の5日目の記事です。 

前回「初めてのTypeScript (2) - jQueryを利用してみる」の続きです。

今度は、jQueryではなく、knockout.jsをTypeScriptで使ってみます。

パッケージのインストール

まずは、NuGetで以下の2つをインストールします。

knockout.js,  knockout.TypeScript.DefinitelyTyped

index.html の定義

htmlをknockout.js用に書き換えます。

<!DOCTYPE html>
<html lang="ja-jp">
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="Scripts/knockout-3.4.0.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <h2>TypeScript HTML App</h2>
    <div data-bind="text: nowTime"></div>
    <br>
    <button data-bind="click: onClick, text:buttonFace" type="button" >Start</button>
</body>
</html>

jQueryは使いませんので、読み込む JavaScriptは、knockout-3.4.0.js と app.js の2つです。

data-bind="..."
という箇所が、knockout用の記述です。id属性は不要です。

ViewModel の定義

前回のGreeter クラスをもとに、GreeterViewModel クラスを定義します。

class GreeterViewModel {
    private timerToken: number = null;
    buttonFace: KnockoutObservable<string> = ko.observable(null);
    nowTime: KnockoutObservable<string> = ko.observable("");
    onClick = () => {
        if (this.isRunning()) {
            this.stop();
        } else {
            this.start();
        }
    };

    constructor(buttonFace: string) {
        this.buttonFace(buttonFace);
    }

    private nowString(): string {
        return "The time is: " + new Date().toUTCString();
    }

    private isRunning() {
        return this.timerToken !== null;
    }

    private start() {
        this.buttonFace("Stop");
        this.timerToken = setInterval(() => {
            this.nowTime(this.nowString());
        }, 500);
    }

    private stop() {
        clearTimeout(this.timerToken);
        this.timerToken = null;
        this.buttonFace("Start");
    }
}

buttonFace, nowTime, onClick がknockoutでバインドするメンバーになります。このGreeterViewModelクラスの中では、DOMを直接操作している箇所はありません。

このビューモデルは、モデルも兼ねてしまっていますが、そこは大目にみてください。

GreeterViewModelを利用する

では、このGreeterViewModelを利用して、プログラムが動くようにします。

残りのコードを以下に示します。

class Application {
    static run() {
        let greeter = new GreeterViewModel("Start");
        ko.applyBindings(greeter);
    }
}

window.onload = () => {
    Application.run();
}

動かしてみる

では、F5キーを押して動かしてみます。

Startボタンを押すと、時刻が1秒ごとに変化していくのが確認できます。 Stopボタンを押すと、時刻の更新が止まります。

  
Posted by gushwell at 22:27Comments(0)TrackBack(0)TypeScript

2016年12月04日

初めてのTypeScript (2) - jQueryを利用してみる


これは TypeScript Advent Calendar 2016 の4日目の記事です。

初めてのTypeScript (1) - 新規プロジェクトを作成してみる」の続きです。

今度は、jQueryを利用してみようと思います。


準備

Visual Studio 2015のNuGetで、jQuery と jQuery 用の jquery.TypeScript.DefinitelyTyped をインストールします。 これだけです。

jQueryを使ってみる。

前回のプログラム(Visual Studioが自動生成する「TypeScriptを使用したHTMLアプリケーション」を jQuery を使って書き換えてみました。

class Greeter {
    private element: JQuery;
    private span: JQuery;
    private timerToken: number;

    constructor(element: JQuery) {
        this.element = element;
        this.element.append("The time is: ");
        this.span = $('<span>');
        this.element.append(this.span);
        this.span.text(new Date().toUTCString());
    }

    start() {
        this.timerToken = setInterval(() => this.span.text(new Date().toUTCString()), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }

}

class Application {
    static run() {
        let greeter = new Greeter($('#content'));
        greeter.start();
    }
}

$(() => {
    Application.run();
});

DOM操作の部分を jQuery を使ったコードに書き換えてみました。HTMLElement型の代わりに、JQuery型を利用しています。

ついでに、staticメソッド使えるのかなということで、Application クラスを定義して、run 静的メソッドを定義しています。

次に、jQueryが使えるように、index.htmlにscriptタグを追加します。

<head>
    ...
    <script src="Scripts/jquery-3.1.1.min.js"></script>
    <script src="app.js"></script>
</head>

これで動かしてみます。無事動きました。

ボタンのクリックイベントを利用する

そういえば、Greeterクラスに定義してある stopメソッドって、定義してるだけで使っていないです。 なので、stopメソッドも使ってみます。

start/stop を行うボタンを配置。初期状態は、"Start"を表示します。

<body>
    <h1>TypeScript HTML App</h1>
    <div id="content"></div>
    <br>
    <button id="startstopButton" type="button">Start</button>
</body>

そして、GreeterクラスにisRunningメソッドを追加します。それに伴い、数か所変更。

class Greeter {
    private element: JQuery;
    private span: JQuery;
    private timerToken: number = null;

    constructor(element: JQuery) {
        this.element = element;
        this.element.append("The time is: ");
        this.span = $('<span>');
        this.element.append(this.span);
    }

    isRunning() {
        return this.timerToken !== null;
    }

    start() {
        this.timerToken = setInterval(() => this.span.text(new Date().toUTCString()), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
        this.timerToken = null;
    }

}

Applicationクラスのrunメソッドを以下のように書き替えます。

class Application {
    static run() {
        let greeter = new Greeter($('#content'));
        $('#startstopButton').on('click', (e) => {
            if (greeter.isRunning()) {
                greeter.stop(); $(e.target).text("Start");
            } else {
                greeter.start(); $(e.target).text("Stop");
            }
        });
    }
}

$(() => {
    Application.run();
});

callback関数を書くのに、いちいち function() { } と書かなくて済むのがとても良いです。

jQuery利用しているところは、JQuery型が導入された以外は、JavaScriptの時とほとんど変わらないですね。
 

動かしてみます。問題なく動いてるようです。

  
Posted by gushwell at 21:00Comments(0)TrackBack(0)TypeScript

2016年12月01日

初めてのTypeScript (1) - 新規プロジェクトを作成してみる


ほんのすこし時間ができたので、久しぶりにブログ更新です。

12/4 追記
TypeScript Advent Calendar 2016があるのをこの記事をアップ後に知りました。
1日目が空いていたので、勝手ながら、TypeScript Advent Calendar 2016 の1日目の記事として登録させていただきました。

-----

TypeScript2.0が正式に公開されたとのことで、TypeScriptに興味が沸いてきたので、ちょこっと勉強してみました。 JavaScriptは初心者に毛が生えたレベル。TypeScriptはほとんど知識ゼロなので、もし、おかしな事書いていたら、指摘していただけるとありがたいです。

準備

Get TypeScript のページの "Visual Studio 2015" のリンクをクリックし、言語で Japanese を選択し、「TypeScript for Visual Studio 2015 - 日本語」をインストールします。

ちなみに、Visual Studio 2015 は、Update3 を適用している必要があるようです。

プロジェクトの作成

Visual Studio 2015を使い、新規プロジェクトの作成で、TypeScriptのプロジェクトを作成してみます。 プロジェクトの種類は、「TypeScriptを使用したHTMLアプリケーション」です。

作成されたプロジェクトには、app.ts というファイルが作成されています。これが、TypeScriptのソースファイルですね。

class Greeter {
    element: HTMLElement;
    span: HTMLElement;
    timerToken: number;

    constructor(element: HTMLElement) {
        this.element = element;
        this.element.innerHTML += "The time is: ";
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.span.innerText = new Date().toUTCString();
    }

    start() {
        this.timerToken = setInterval(() => this.span.innerHTML = new Date().toUTCString(), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }

}

window.onload = () => {
    var el = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};

あわせて、index.htmlファイルも作成されています。

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="app.js"></script>
</head>
<body>
    <h1>TypeScript HTML App</h1>

    <div id="content"></div>
</body>
</html>

scriptタグで、app.tsから変換されたJavaScriptのファイル app.js を読み込んでいます。 それ以外は、特筆すべき点はない、htmlファイルです。

読み込んでいるスタイルシートapp.cssは、こんな感じ。

body {
    font-family: 'Segoe UI', sans-serif;
}

span {
    font-style: italic;
}

app.tsを読んでみる

app.tsのコードを読んでみます。このコードで特徴的なのは、classを定義していること。それと、=> 使っていること。arrow式/arrow関数って言うらしいです。

C#プログラマには、とてもわかりやすいですね。JavaScriptをあまり知らなくても、何をやっているかがわかります。elementspantimerTokenは、フィールド、start(), stop() がメソッドですね。

C#と違って、変数宣言は、

HTMLElement element;  

ではなくて、

element: HTMLElement;  

なんですね。Pascalみたいです。

それと、コンストラクタは、クラス名ではなく、constructor キーワードを使うんですね。 C#のコードを見ていると、どれがコンストラクタなのか、パッと見にはわからないので、この書き方の方がスマートのように感じます。

早速、F5キーを押して動かしてみます。

おお、無事に動きました。って、何もコードいじってないので当たり前ですね。

ということは、Visual Studioが自動で、JavaScriptに変換してくれているんですね。

TypeScriptは、メンバーの可視性のデフォルトは、public みたいです。ここはC#と大きく異なるところですね。

ちょっと変更

そういえば、privateって使えるのかな? フィールドは、classの外から見る必要はないので、privateにしてみます。

private element: HTMLElement;
private span: HTMLElement;
private timerToken: number;

ビルドも通りました。

Visual Studio のエディタで、greeter. とタイプした時に表示される候補一覧の中には、もう、privateにしたメソッドは出てきません。

さらに、startメソッドの中の、this.timerTokenthis. を取り去ってみました。そしたら、ビルドエラー! C#と違って、this. は省略できないみたいです。

変換後のJavaScriptファイル

最後に、変換された jsファイルを載せておきます。このjs ファイルは、プロジェクトには追加されませんが、tsファイルと同一フォルダにありました。

var Greeter = (function () {
    function Greeter(element) {
        this.element = element;
        this.element.innerHTML += "The time is: ";
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.span.innerText = new Date().toUTCString();
    }
    Greeter.prototype.start = function () {
        var _this = this;
        this.timerToken = setInterval(function () { return _this.span.innerHTML = new Date().toUTCString(); }, 500);
    };
    Greeter.prototype.stop = function () {
        clearTimeout(this.timerToken);
    };
    return Greeter;
}());
window.onload = function () {
    var el = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};

なるほど、startなどのメソッドは、prototypeに登録してくれているんですね。継承については特別なコードを書く必要はないということですかね。

JavaScript書く時にいつも混乱するthisですが、TypeScriptのthis とJavaScriptのthisは、違うってのも、このコード読むと分かりますね。

ちょっと驚いたのは、elementtimerTokenというフィールド定義が、JavaScriptのコードからは、なくなってしまっていること。

このあたりがJavaScriptらしさなのかな? C#プログラマの僕にはちょっと気持ち悪いです。

JavaScript と比べると、TypeScriptは行数が多めですが、見た目のごちゃごちゃ感が無くて、かえってすっきりしている気がします。ラムダ式(TypeScriptではアロー式)も使えるし、C#プログラマーにもわかりやすいです。

  
Posted by gushwell at 21:36Comments(0)TrackBack(0)TypeScript

2016年06月11日

現在、C#の本を執筆中です


タイトルの通り、現在C#プログラミングの本を執筆中です。

ブログの更新をしばらくサボっていますが、原稿書きに時間を充てているのが、更新できていない理由の一つでもあります。

内容については、出版社(技術評論社様)とのお約束で、ここで触れることができないのが残念ですが、
今年の秋から冬くらいには出せるようにと頑張っているところです。

ということで近況報告でした。

そのうち、ブログも更新したいなーとは思っています。...

---
2016/10/01 追記
 編集者の方から連絡があり、諸般の事情で出版は来年になるとのことでした。

   
Posted by gushwell at 11:44Comments(4)TrackBack(0)雑談

2016年04月02日

Microsoft MVP アワード再受賞しました  



本年も、Microsoft MVP アワードを無事、再受賞することができました。 
おかげさまで 2005年4月からの連続受賞で今回で12回目となりました。やはり何回頂いても嬉しくそして、ありがたいものです。

カテゴリーは、「Visual Studio and Development Technologies」です。 これまでは、C# → .NET でしたが、今回カテゴリーが変更になりました。

以下受賞メールの一部抜粋です。
34


この時期になると毎年思いますが、いやーほんとに長い期間良く続けられたなーと思います。
一昨年に終了したC#メールマガジンのバックナンバーのダウンロードページには、未だにそれなりのアクセスがありますので、C#プログラマーの方々のスキルアップにそれなりに貢献できたのかなと思います。


今年に入りブログが放置状態ですが、開発者としてはまだまだ現役ですので、また何かアップできればと思っています。


  
Posted by gushwell at 11:20Comments(0)TrackBack(0)雑談