$ ryokkkke.com/TypeScript/tsconfig.json

downlevelIteration

概要

https://www.typescriptlang.org/tsconfig#downlevelIteration

"downlevelIteration": true

Downleveling(ダウンレベリング)というのは、古いバージョンの JS コードにコンパイルするという意味の TypeScript 用語です。

targetES3またはES5の際に、ジェネレータのyield*for..of構文などのES6から追加されたイテレーション系の記法を、より正確なコードにコンパイルしたい場合にtrueにします。

TypeScript 2.3 で導入されました。

詳細

ES6 で、JS にはイテレータ(Symblo.iterator)と共に

  • for-ofループ
  • 配列展開([a, ...b]
  • 引数展開(fn(..args)

といったイテレーション系の記法が追加されました。

これらの記法はデフォルトでも基本的には問題なくコンパイルできますが、一部の条件でコンパイルエラーになります。
それらの記法は内部でイテレータを使用しているわけですが、ES5 以下の古い仕様にはそもそもイテレータが存在しないので、簡易なコードでは完全に再現できず、特定の条件でランタイムエラーを吐きます。

それを防ぐために、このオプションをtrueにすると、実行時にSymbol.iteratorを探し存在すれば反復処理にイテレータを使うようになる Polyfill を JS コードに追加します。
この際、Symbol.iteratorは実行環境のネイティブ実装でもサードパーティーの Polyfill でも大丈夫です。

逆に言うと、実行環境にSymbol.iteratorの実装が存在しなければ、このオプションをtrueにしてコンパイルが通ったところで、コンパイル結果のコードは依然としてランタイムエラーの危険性を持ったままになります。
これ以上細かい話は公式ドキュメントをご覧ください。

コンパイルエラーの例

たとえば、配列・文字列以外の iterable なオブジェクトに対してこれらの新しい記法を実行しようとすると、targetES3ES5の場合にコンパイルエラーになります。

// index.ts

// これは配列なのでOK
const arr = [1, 2, 3];
for (const value of arr) {
  console.log(value);
}

// Setはiterableだけど配列ではないのでエラー
const map = new Set([1, 2, 3]);
for (const value of map) {
  console.log(value);
}
src/index.ts:9:21 - error TS2569: Type 'Set<number>' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.

他にも、ジェネレータのyield*を配列・文字列以外で使用すると同様のエラーが出ます。

// index.ts

// これは配列なのでOK
function* func() {
  while (true) {
    const arr = [1, 2, 3];
    yield* arr;
  }
}

// Mapはiterablだけど配列・文字列ではないのでエラー
function* func() {
  while (true) {
    const map = new Map<string, string>();
    map.set("hoge", "HOGE");
    yield* map;
  }
}
src/index.ts:14:12 - error TS2569: Type 'Map<string, string>' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.

ちなみに、これは自作の iterable なオブジェクトでも同様です。

// index.ts

class MyIterator {
  count = 0;
  max: number;

  constructor(max: number) {
    this.max = max;
  }

  next() {
    const done = this.count > this.max;
    const value = done ? undefined : this.count;

    this.count = this.count + 1;

    return { value, done };
  }
}

const iterator = new MyIterator(10);

// iterableなオブジェクトは[Symbol.iterator]プロパティがイテレータを返す必要がある。
// 参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Iteration_protocols
const iterable = {
  [Symbol.iterator]() {
    return iterator;
  },
};

// `iterable`オブジェクト はiterableだけど配列ではないのでエラー
for (const value of iterable) {
  console.log(value);
}

// `iterable`オブジェクト はiterableだけど配列ではないのでエラー
function* func() {
  while (true) {
    yield* iterable;
  }
}

イテレータも iterable なオブジェクトも自分で書いたとして、このコードのコンパイル結果は

src/index.ts:29:21 - error TS2569: Type '{ [Symbol.iterator](): MyIterator; }' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.

です。

これらは、"downlevelIteration": trueにすることでコンパイルが通るようになります。