はじめに
Knockoutの公式ページ[http://knockoutjs.com/]
のTutorialであるWorking with Lists and CollectionsをTypeScriptを使って記載しています。ソースコードは、GitHubにアップしています。
Visual Studioを使ったデバッグ方法
今回はVisual Studioを用いたデバッグ方法について記載します。 まずは、いつものように、 Knockout Tutorial Working with Lists and Collectionsの JavaScriptのソースコードをTypeScriptに書き直します。
<Knockout Tutorial Working with Lists and Collectionsのソースコード>
self.formattedPrice = ko.computed(function() { var price = self.meal().price; return price ? "$" + price.toFixed(2) : "None"; }); }
<上記ソースコードをTypeScriptに書き直したもの(バグあり)>
formatterPrice: KnockoutComputed<string> = ko.computed(() => { var price = this.meal().price; return price ? "$" + price.toFixed(2) : "None"; }); }
これをVisual Studioで実行すると、エラーで止まります。 エラー内容は、未定義またはNULL参照のプロパティ'price'は取得できません。
これだけ見てもさっぱりわからないので、解析に移ります。
まず、プログラムが止まっている箇所は、var price = this.meal().price;
の行です。
このときのthis.mealの値を確認してみましょう。
イミディエイトウィンドウを使います。
[イミディエイト] ウィンドウは、式のデバックと評価、ステートメントの実行、変数値の出力などのために使用します。
なお、デバッグ時にしか、使用できません。
this.meal
と入力すると、undefined
と返ってきました。
初回のformatterPriceが呼び出されたときは、this.mealはundefinedのようです・・・。
今度は、KnockoutComputed型のformatterPriceプロパティが正しく動作しているか確認します。 (this.meal()の中身を確認するために、JSON.stringifyを使用)
formatterPrice: KnockoutComputed<string> = ko.computed(() => { //var price = this.meal().price; //return price ? "$" + price.toFixed(2) : "None"; return JSON.stringify(this.meal()); });
実行すると、正しく設定が表示されました。
次は、実行順序を確認します。
下記のように、SeatReservationクラスのインスタンスを生成(new)している箇所と
this.mealを初期化しているSeatReservationのコンストラクタと
var price = this.meal().price;
の行にブレークポイントを仕掛けます。
この状態で実行すると、なんということでしょう。
SeatReservationクラスのコンストラクタの初期化処理this.meal(item);
が実行される前に、
KnockoutComputed型のformatterPriceプロパティの処理が実行されています。
つまり、コンストラクタ内の初期化処理に入る前に、
KnockoutComputed型のformatterPriceプロパティの処理が実行されていることが今回のバグの原因です。
対策
対策としては、this.meal()がundefinedの場合は、"None"で返すのがベストです。 なお、this.mealとするとメソッドそのものを指してしまうので、誤りです。
<上記ソースコードをTypeScriptに書き直したもの(正常動作するもの)>
formatterPrice: KnockoutComputed<string> = ko.computed(() => { if ( this.meal() === undefined ) { return "None"; } else { var price = this.meal().price; return price ? "$" + price.toFixed(2) : "None"; } });
正常に実行された結果は以下のとおり。 なお、Standard(sandwich)のPriceは、ここでは、10ドル(元はNone)に設定しております。
まとめ
原因不明のバグにぶつかったときには、以下の方法が有効です。