自動テスト用に引数で受け取ったものをそのまま返すArray.of
のgenerator版が必要になりました。
単純な書き方
namespace Generator {
export function* of0(...args: any[]) {
yield* args;
}
}
const g0 = Generator.of0(1, '2', true, false, null, undefined, [1], {a:1});
// g0: IterableIterator<any>
適当に作ったのはいいんですが、せっかくTypeScriptで書くのだから、受け取った引数の型を返値の型に反映したいですね。
素直にジェネリクスを使ってみる
namespace Generator {
export function* of1<T>(...args: T[]) {
yield* args;
}
}
//const g1 = Generator.of1(1, '2', true, false, null, undefined, [1], {a:1});
// 違う型を指定すると2番目の引数(正確には2種類目の型の引数)でエラー
const g1 = Generator.of1(1,2,3); // 一種類の型しか指定できない
// g1: IterableIterator<number>
うーむ…一種類だけしか指定できないのではデグレードです。
もうちょっと工夫してジェネリクスを使う
namespace Generator {
export function* of2<T extends any[]>(...args: T) {
yield* args;
}
}
const g2 = Generator.of2(1, '2', true, false, null, undefined, [1], {a:1});
// g2: IterableIterator<any> // やっぱりanyになってしまう
元に戻ってしまいました。
Conditional typesと infer を使う(TypeScript 2.8以降)
namespace Generator {
export function* of<T extends any[]>(...args: T): IterableIterator<T extends (infer R)[] ? R : never> {
yield* args;
}
}
const g = Generator.of(1, '2', true, false, null, undefined, [1], {a:1});
// g: IterableIterator<string | number | boolean | number[] | {a: number;} | null | undefined>
// やった!
ようやくできました。
(おまけ) Array.concatのgenerator版
concat
も同じような感じで作れます。
※正確にはArrayのconcatはインスタンスメソッドですが
namespace Generator {
export function* concat<T extends IterableIterator<any>[]>(...generators: T): IterableIterator<T extends IterableIterator<infer R>[] ? R : never> {
for (const g of generators) {
yield* g;
}
}
}
const gg = Generator.concat(
Generator.of(1, '2', true, false),
Generator.of(null, undefined, [1], {a:1})
);
// gg: IterableIterator<string | number | boolean | number[] | {a: number;} | null | undefined>