🗜️

TypeScriptでパイプを作ってみる【※ネタです】

2021/09/27に公開

はじめに

TypeScriptでもパイプライン演算子が使いたい!!!

はい、ということでネタ記事を書きました。
TypeScriptではよくクラスではなくオブジェクトを使う人がいると思います。
自分もオブジェクト派です。
スプレッド演算子が便利だったり、フロントエンド特有の状態管理ストアへの書き込みが楽だったりするからです。

しかし当たり前ですがオブジェクトだとふるまいを保持できないため外出しの関数を脇に置く形になると思います。

user.ts
type User = {
  userId: number;
  userName: string;
};

const updateUser = (user: User, newUser: Partial<User>) => {
  return {
    ...user,
    ...newUser,
  };
};

const user = { userId: 1, userName: 'Hanako' };

updateUser(user, {
  userName: 'Taro'
});

しかしこれだと続けて関数を適用しようとするとカッコが重なり読みづらくなります

// カッコが多くて読みづらい
getUserName(updateUser(user, {
  userName: 'Taro'
}));
// => 'Taro'

// クラスだとメソッドチェーンできる
user
  .updateUser({
    userName: 'Taro'
  })
  .getUserName()
// => 'Taro'

一部の関数型言語(Elmとか)だとパイプなるものがあり、こんな感じで書けます。
関数や値の結果を右に受け渡すシェルとかのパイプと同じです。

user
  |> updateUser({
    userName: 'Taro'
  })
  |> getUserName
// => 'Taro'

詳細はこの記事がわかりやすかったです。
https://qiita.com/mather314/items/1d5917b2b9f415d18372

メソッドチェーンに似てますね。使いやすい。。。
当たり前ですが、TypeScriptにはこのようなパイプというものがないので作ります。

※9割ネタなのでプロダクションコードでは絶対にマネしないでください。

パイプクラスを作る

関数型を目指すためにオブジェクト指向のクラスを使わないといけないなんて皮肉だ、、。

|> に当たる pipe メソッドをはやします。

pipe.ts
class Pipe<T> {
  constructor(public value: T) {}

  pipe<S>(f: (value: T) => S): Pipe<S> {
    return new Pipe(f(this.value));
  }
}

関数を定義していく

メソッドチェーンみたいに使いたい関数は高階関数として定義します(初めから高階関数として定義しているのでカリー化とはいわないかもしれない)。

user.ts
type User = {
  userId: number;
  userName: string;
};

// 更新パラメータを受け取って、「Userを受け取ってUserを返す関数」を返す関数
const updateUser = (newUser: Partial<User>) => (user: User) => {
  return {
    ...user,
    ...newUser,
  };
};

// Userを受け取ってユーザー名を返す関数
const getUserName = (user: User) => {
  return user.userName
}

実際にパイプを使ってみる

main.ts
const user = { userId: 1, userName: 'Hanako' };

const updatedUser = new Pipe(user) // パイプの元を作成
  .pipe(updateUser({  // パイプで関数をつないでいく
    userName: 'Taro',
  }))
  .pipe(getUserName)
  .value; // Pipeインスタンスを最後返るのでvalueプロパティから元のオブジェクトを取得

console.log(updatedUser) 
// => 'Taro'

おーできた、できた。ちょっとは使用感がパイプに近づいたかも知れないですね。

こんなもんはパイプじゃねえ! という有識者の声が聞こえそうですが、僕の脳ではこれが限界です。
そもそもパイプは式ではなく文?なので(|> などのパイプ自体は値を返却できない)組み込みの文のサポートが無い限りはそれっぽい形にしかならないです。

ちなみに単純な関数合成はTypeScriptでも綺麗に実装できるので、それくらいにとどめておくのがいいかもしれないですね。

https://qiita.com/Nossa/items/a8b9e013eb0467321c1e

Haskellとかちゃんと学べばもっといいのができるのかもしれない、、、

Discussion