Typescript でメソッドチェーン活用:ネストしたデータ構造からの値抽出を簡潔に
こんにちは。Gaji-Labo の村上です。
データから特定の値を抽出する際、コードが冗長になったり可読性が低下したりして困ったことはありませんか?
特に外部 API からのレスポンスや複雑な設定情報といった、ネストしたデータ構造を扱う場面ではこうした課題に直面することが多いのではないでしょうか。
そんな時は、メソッドチェーンを活用することで、これらの課題を解決できるかもしれません。
この記事では、実際のコード例を通して、TypeScript の配列操作メソッドを活用したデータ抽出がどれだけ書きやすくなるかを紹介します。
なお、今回は `map`、`filter`、`flatMap`、`some` などの配列メソッドを中心としたメソッドチェーンに絞って解説します。
今回扱う課題:複雑な API レスポンスからの値抽出
以下のような複雑な入れ子構造の API レスポンスを例に考えてみましょう。
type ApiResponse = {
data: {
departments: {
id: string;
name: string;
type: "sales" | "engineering" | "marketing";
teams: {
teamId: string;
teamName: string;
isActive: boolean;
members: {
userId: string;
name: string;
role: string | null;
permissions: {
level: "read" | "write" | "admin" | null;
scope: string;
};
}[];
}[];
}[];
};
};
この構造から「アクティブなチームで`level`が”admin”または”write”の権限を持つユーザーが存在するか」を判定したいとします。
メソッドチェーンでの実装
それでは、先ほどの課題をメソッドチェーンで解決してみましょう。
ステップ 1:ヘルパー関数を作成
まず、小さくて再利用可能なヘルパー関数を作ります。
// null/undefinedチェック用のタイプガード
const isNotNullish = <T>(value: T | null | undefined): value is T =>
value !== null && value !== undefined;
// 権限レベルの妥当性チェック
const isValidPermission = (level: string): boolean =>
level === "admin" || level === "write";
// アクティブなチームかどうかのチェック
const isActiveTeam = (team: { isActive: boolean }) => team.isActive;
ステップ 2:メイン処理を実装
次に、作成したヘルパー関数と配列メソッド(flatMap
、filter
、map
、some
)を組み合わせて、メイン処理を実装します。
メソッドチェーンを使うことで、データの変換処理を順序立てて記述できます。
const hasValidPermission = (response: ApiResponse): boolean =>
response.data.departments
.flatMap((dept) => dept.teams) // ステップ1: teamsを平坦化
.filter(isActiveTeam) // ステップ2: アクティブなチームのみ
.flatMap((team) => team.members) // ステップ3: membersを平坦化
.map((member) => member.permissions) // ステップ4: permissionsを抽出
.map((permission) => permission.level) // ステップ5: levelを抽出
.filter(isNotNullish) // ステップ6: null/undefined除去
.some(isValidPermission); // ステップ7: 条件チェック
このように、メソッドチェーンを使うことで処理の流れが上から下に読めて、簡単で見やすいコードになります。
なお、メソッドチェーンは各ステップで配列の全要素を処理するため、大量のデータを扱う場合は処理時間が長くなる可能性があります。
ただし、一般的な Web アプリケーションの規模であれば、パフォーマンスよりも可読性を取るのも選択肢の一つです。
小さな関数のテストが書きやすい
メソッドチェーンと小さな関数の組み合わせの大きなメリットの一つが、テストの書きやすさです。
小さな関数に分けることで、複雑な処理全体をテストするのではなく、個別の機能を独立してテストできます。
例えば、`isValidPermission`関数は単純な入力と出力の関係なので、様々なケースを簡単にテストできます。
describe("isValidPermission", () => {
test("adminレベルの場合はtrueを返す", () => {
expect(isValidPermission("admin")).toBe(true);
});
test("writeレベルの場合はtrueを返す", () => {
expect(isValidPermission("write")).toBe(true);
});
test("readレベルの場合はfalseを返す", () => {
expect(isValidPermission("read")).toBe(false);
});
});
さいごに
TypeScript のメソッドチェーンを活用することで、複雑なデータ構造からの値抽出が効率的に書けることがわかりました。処理の流れが一目でわかり、テストも書きやすく、コード全体がスッキリします。
特に以下のような時に活用できそうです。
- ネストした API レスポンスを扱うとき
- 複数の条件でデータをフィルタリングするとき
- 中間変数が増えがちな複雑な処理を書くとき
どのような時でもメソッドチェーンが適切とは限りませんが、コードの可読性を担保する有効な選択肢として覚えておくと良さそうです。
Gaji-Labo では、効率的で持続可能な開発手法でスタートアップのプロダクト開発を支援していますので、興味のある方はお気軽にカジュアル面談をお申し込みください!
Gaji-Labo フロントエンドエンジニア向けご案内資料
Gaji-Laboでは、 Next.js 経験が豊富なフロントエンドエンジニアを募集しています
弊社では Next.js の知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違う Gaji-Labo を味わいに来ませんか?
Next.js の設計・実装を得意とするフロントエンドエンジニア募集要項
もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!