はじめに
Java8から追加されたStream APIのpeek
をTypeScriptでする場合の実装例です。
C#の拡張メソッドのようにArrayにpeek
というメンバーを追加することで実現していきます。
方法
TypeScript1.8から追加されたGlobal augmentationを使います。
これをすることで、補完機能や型チェックを効かせながらpeek
を使用できるようにします。
#global-augmentation Declaration Merging · TypeScript
実装例
ArrayAugmentation.ts
declare global {
interface Array<T> {
// JavaのStream#peekを参考に実装
peek(
callbackfn: (value: T, index: number, array: T[]) => void,
thisArg?: any
): T[];
}
}
// プロトタイプ汚染しないように`peek`を拡張。
// コード自体はJavaScriptのときと同じ書き方。
// TypeScriptでもArray.prototype.peek = function...みたいに実装してしまうとプロトタイプ汚染になってしまう。
Object.defineProperties(Array.prototype, {
peek: {
value: function(callbackfn) {
this.forEach(callbackfn);
return this;
}
}
});
// ReadonlyArray<T>やTypedArray(Int8Arrayなど)はArray<T>と継承関係はないため、
// もし、そちらでも使いたい場合は両方ともに実装する必要があります。
// 今回は省略。
// `declare global`を使って拡張する場合は必要となる。
export {};
PeekSample.ts
// peekを実装したモジュールを名前指定せずにimportする。
import "./ArrayAugmentation";
// 以下、peek関数の検証。
// 参考) peekの使い方。
// http://marxsoftware.blogspot.com/2018/06/peeking-inside-java-streams.html
class Person {
constructor(
public lastName: string,
public fistName: string,
public age: number = 0
) {}
}
// ログの出力形式。
const logMessage = function(e: Person): void {
console.log(`${e.lastName}${e.fistName}、${e.age}歳。`);
};
// 赤の他人2人を初期化
const perfectStrangers = [
new Person("野原", "ひろし", 27),
new Person("小山", "みさえ", 21)
];
perfectStrangers
.peek(logMessage)
// 1年後
.peek(e => (e.age += 1)) // 正確には結婚時期は未定。
.peek(e => (e.lastName = "野原"))
.peek(logMessage)
// 2年後
.peek(e => (e.age += 2))
.concat(new Person("野原", "しんのすけ"))
.peek(logMessage)
// 5年後
.peek(e => (e.age += 5))
// シロも追加するべきだがPersonではないため省略。
.concat(new Person("野原", "ひまわり"))
.forEach(logMessage);
おわりに
peek
はforEach
同様、副作用のある処理を実行することになるので多様は厳禁です。
デバッグ時にログ出力するために使うなど、使用場所は限定しておいた方がいいと思います。
あと、余談ですが、peek
と聞くと、java.util.Deque#peekかと思ってしまいます。
peekという英単語が「そっとのぞく、ちらっと見る」という意味になるので、確かにどちらも間違いではないですね。
参考
Declaration Merging · TypeScript
プロトタイプ汚染とループ - latest log
Peeking Inside Java Streams with Stream.peek
Stream (Java SE 10 & JDK 10 )