TypeScript 1.5でのlet文

ECMAScriptの変数のスコープが不思議な挙動をするのはよく知られた話だ。ECMAScript 5での変数のスコープは以下のいずれかに大別される(はず)。ブロックスコープがない。

  • グローバルスコープ
  • 関数スコープ
  • catchスコープ

普通に使う変数が関数スコープしかないのは不便、ということで、MozillaはJavaScript 1.7(2006年頃)でlet文を導入した。が、しかし、他のブラウザには普及せず、Mozillaの独自拡張に留まることになった。

時は過ぎ、ECMAScript 5が制定され、次はECMAScript 6という流れになってきた。そして、ECMAScript 6にはlet文が導入されることになった(MozillaがJavaScript 1.7で導入したものとは微妙に仕様が違う)。めでたしめでたし。

ECMAScriptに静的型を付けたTypeScriptは、当初はlet文をサポートしていなかった。それが、TypeScript 1.4で、ECMAScript 6へコンパイルする場合のみlet文をサポートするようになった。

が、ECMAScript 6(のlet文)に対応していないブラウザはまだまだたくさんある(今調べてみたら、IE11ではES6のlet文に対応しているけど、Safariでは対応していないようだ)。できれば、TypeScriptのようなaltJS言語でlet文を使いつつ、コンパイル後のJavaScriptでは従来のvarを使ってほしい。

が、let文を従来のJavaScript (ECMAScript 5)に変換するのはそう自明なことではない。特に問題になるのは、ループ内の変数を、ループの外に持ち出される関数で使っている場合だ。

var a = [];
for (var i = 0; i < 5; ++i) {
    let x = i * i;
    a.push(function() { return x; });
}

上のコードを下のように単純にvarを使うと挙動が変わってしまう。

var a = [];
for (var i = 0; i < 5; ++i) {
    var x = i * i;
    a.push(function() { return x; });
}

このコードを正しくECMAScript 5に翻訳するには、即時実行関数(IIFEを使って以下のようにしなければならない。

var a = [];
for (var i = 0; i < 5; ++i) {
    (function() {
        var x = i * i;
        a.push(function() { return x; });
    }());
}

こういう非自明、というかパフォーマンスに影響するところがあるので、TypeScript 1.4ではES5へのコンパイル時にlet文がサポートされなかったのだろう。

が、しかし、TypeScript 1.5ではES5へコンパイルする場合でもlet文が部分的にサポートされるようになるようだ。「部分的に」というのは、ループが絡まない、変数のスコープのチェックと名前の変更で済む場合、のようだ。

{
    let x = 123;
    console.log(x); // OK
}
console.log(x); // エラー

ループが絡む、IIFEを使った変換が必要な場合は、

test2.ts(2,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher.

というエラーが出た。

let a: Array<() => number> = []
for (var i = 0; i < 5; ++i) {
    let x = i*i;
    a.push(() => x);
}
a.forEach(console.log);

GitHubでの該当するIssue/Pull Requestは以下のようだ。

let文のサポートとしては不完全とはいえ、varの関数スコープのせいで意図しない同名の変数を参照していた〜〜みたいな事故は防げると思われるので、積極的に使っていきたい。


TypeScript 1.5でのlet文」への1件のフィードバック

  1. ピンバック: TypeScript における let/const と control flow based type analysis | 雑記帳

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です