SODA Engineering Blog
🎉

TypeScriptで関数型プログラミングを行ってみた [fp-ts]

2024/09/30に公開

こんにちは。FEチームのMapleです。私たちのチームは、現在のシステムアーキテクチャを見直し、Reactを用いた新しいアーキテクチャへの移行を検討しています。今回は先日TypeScriptで行う関数型プログラミングについて解説していきます。

はじめに

関数型プログラミングは、不変性(Immutability)と純粋関数(Pure Functions)を重視し、副作用を最小限に抑えることで、予測可能でテストしやすいコードを書いていきます。

  • バグの減少:副作用が少ないため、予期せぬ動作を防げる。
  • コードの再利用性:純粋関数は独立性が高く、再利用が容易。
  • テスト容易性:関数が同じ入力で常に同じ出力を返すため、ユニットテストが簡単。

TypeScriptでの関数型プログラミングの基本

不変性の確保

TypeScriptでは、constやreadonlyを使って不変性を確保できます。

const x = 10;
// x = 5; // エラー

interface User {
  readonly id: number;
  readonly name: string;
}

const user: User = { id: 1, name: 'Alice' };
// user.id = 2; // エラー

純粋関数の作成

純粋関数は、副作用がありません。

const add = (a: number, b: number): number => a + b;

高階関数とラムダ式

関数を引数や戻り値として扱う高階関数を利用できます。

const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map((n) => n * n);

実践:データ処理に関数型アプローチを適用

問題

ユーザーのリストから特定の条件に合うユーザーを抽出し、その名前のリストを取得したい。

関数型での解決策

  • フィルタリング:条件に合うユーザーを抽出する純粋関数を作成。
  • マッピング:ユーザーオブジェクトから名前を取り出す純粋関数を作成。
  • 合成:上記の関数を組み合わせて目的の結果を得る。
interface User {
  id: number;
  name: string;
  isActive: boolean;
}

const users: User[] = [
  { id: 1, name: 'Alice', isActive: true },
  { id: 2, name: 'Bob', isActive: false },
  { id: 3, name: 'Carol', isActive: true },
];

const getActiveUsers = (users: User[]): User[] =>
  users.filter((user) => user.isActive);

const getUserNames = (users: User[]): string[] =>
  users.map((user) => user.name);

const activeUserNames = getUserNames(getActiveUsers(users));
console.log(activeUserNames); // ["Alice", "Carol"]

ライブラリの活用

TypeScriptでより高度な関数型プログラミングを行うために、fp-tsというライブラリを利用しました。

import { Option, some, none } from 'fp-ts/Option';

const findUserById = (id: number): Option<User> => {
  const user = users.find((user) => user.id === id);
  return user ? some(user) : none;
};

findUserById(1).fold(
  () => console.log('User not found'),
  (user) => console.log(`Found user: ${user.name}`)
);

副作用の管理

副作用を持つ操作を明示的に扱うために、IO型を使用しました。

import { IO } from 'fp-ts/IO';

const log = (message: string): IO<void> => () => console.log(message);

const program: IO<void> = log('Hello, Functional Programming!');
program(); // "Hello, Functional Programming!" と表示

まとめ

TypeScriptで関数型プログラミングを実践することで、コードの品質と保守性が大幅に向上しました。特に、純粋関数や高階関数、不変性の概念を取り入れることで、バグの発生を抑えつつ、読みやすいコードを書くことができました。

関数型プログラミングに興味のある方は、ぜひTypeScriptで触ってみてください!!

SODA Engineering Blog
SODA Engineering Blog

Discussion