lodash vs radash — ユーティリティライブラリ徹底比較
1. 結論
新規プロジェクトでモダンなTypeScript環境を使うなら radash を第一候補にしてください。 既存の大規模プロジェクトで長年の実績・網羅的なAPI・豊富な情報を重視するなら lodash(特に lodash-es)が依然として堅実な選択です。どちらも「配列・オブジェクト・関数のユーティリティ集」という同じ領域を担いますが、設計思想と時代背景が大きく異なります。
2. 比較表
| 観点 | lodash | radash |
|---|---|---|
| 初回リリース | 2012年 | 2022年 |
| 最新バージョン(2025年時点) | 4.17.21 | 12.x |
| npm 週間DL数 | 約5,000万〜 | 約50万〜 |
| バンドルサイズ(フル) | 約 71 kB (min+gzip) | 約 4 kB (min+gzip) |
| Tree Shaking | lodash-es で対応 | ネイティブ対応(ESM) |
| TypeScript対応 | @types/lodash が必要 | ソースがTypeScriptで書かれている |
| 関数の数 | 300+ | 90+ |
| 設計思想 | 網羅的・汎用的 | 厳選・モダン・型安全 |
| ミュータブル操作 | 一部あり(_.set 等) | 原則イミュータブル |
| 非同期ユーティリティ | ほぼなし | tryit, parallel, retry 等あり |
| 学習コスト | 低(情報が豊富) | 低(APIが少なく直感的) |
| ライセンス | MIT | MIT |
3. それぞれの強み
lodash の強み
- 圧倒的な実績と安定性: 10年以上の歴史があり、Fortune 500企業を含む無数のプロダクションで稼働しています。
- 網羅的なAPI: 300以上の関数があり、「こんなユーティリティないかな?」と思ったらほぼ見つかります。
- 情報量の多さ: Stack Overflow、ブログ記事、書籍など、日本語を含む情報が圧倒的に豊富です。
- エコシステム連携: Webpack の
lodash-webpack-pluginや Babel のbabel-plugin-lodashなど、最適化ツールが充実しています。 _.chainによるメソッドチェーン: 複雑なデータ変換をパイプライン的に記述できます。
radash の強み
- TypeScriptファースト: ソースコード自体がTypeScriptで書かれており、型推論が非常に正確です。
@types/*のバージョン不一致に悩むことがありません。 - 極めて小さいバンドルサイズ: ESMネイティブ設計のため、Tree Shakingが完璧に効きます。使った関数だけがバンドルに含まれます。
- イミュータブル設計: すべての関数が元のデータを変更しません。React / Vue などの状態管理と相性が良いです。
- 非同期ユーティリティの充実:
retry、parallel、tryitなど、モダンなasync/awaitベースの関数が組み込まれています。 - ゼロ依存: 外部依存パッケージが一切ありません。
4. コード例で比較
4-1. 配列のグルーピング
// === lodash ===
import groupBy from "lodash/groupBy";
interface User {
name: string;
role: "admin" | "member";
}
const users: User[] = [
{ name: "Alice", role: "admin" },
{ name: "Bob", role: "member" },
{ name: "Carol", role: "admin" },
];
const grouped = groupBy(users, "role");
// => { admin: [Alice, Carol], member: [Bob] }
// ⚠ 戻り値の型は Dictionary<User[]> — キーの型が string に広がる
// === radash ===
import { group } from "radash";
interface User {
name: string;
role: "admin" | "member";
}
const users: User[] = [
{ name: "Alice", role: "admin" },
{ name: "Bob", role: "member" },
{ name: "Carol", role: "admin" },
];
const grouped = group(users, (u) => u.role);
// => { admin: [Alice, Carol], member: [Bob] }
// ✅ 戻り値の型は Partial<Record<"admin" | "member", User[]>> — 型が正確
4-2. オブジェクトの特定キー抽出(pick)
// === lodash ===
import pick from "lodash/pick";
const config = { host: "localhost", port: 3000, debug: true };
const result = pick(config, ["host", "port"]);
// => { host: "localhost", port: 3000 }
// ⚠ 型は Partial<typeof config> 相当で、存在しないキーを渡してもエラーにならない
// === radash ===
import { pick } from "radash";
const config = { host: "localhost", port: 3000, debug: true };
const result = pick(config, ["host", "port"]);
// => { host: "localhost", port: 3000 }
// ✅ 存在しないキーを渡すとコンパイルエラーになる
4-3. 非同期リトライ処理
// === lodash ===
// lodash には該当する関数がないため、自前で実装する必要がある
async function fetchWithRetry(
fn: () => Promise<string>,
retries: number
): Promise<string> {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (e) {
if (i === retries - 1) throw e;
}
}
throw new Error("Unreachable");
}
const data = await fetchWithRetry(() => fetch("/api").then((r) => r.text()), 3);
// === radash ===
import { retry } from "radash";
const data = await retry({ times: 3, delay: 1000 }, async () => {
const res = await fetch("/api");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.text();
});
// ✅ リトライ回数・遅延・バックオフを宣言的に指定できる
4-4. 安全なエラーハンドリング
// === lodash ===
// lodash には該当する関数がない
let result: string;
try {
result = JSON.parse(untrustedInput);
} catch (e) {
result = "default";
}
// === radash ===
import { tryit } from "radash";
const [err, result] = await tryit(async () => {
return JSON.parse(untrustedInput) as string;
})();
if (err) {
console.error("パース失敗:", err);
}
// ✅ Go言語風のエラーハンドリングで try-catch のネストを避けられる
5. どちらを選ぶべきか — ユースケース別の推奨
radash を選ぶべきケース
| ユースケース | 理由 |
|---|---|
| 新規のTypeScriptプロジェクト | 型安全性が段違いで、設定不要で恩恵を受けられる |
| フロントエンド(React / Vue / Svelte) | バンドルサイズが小さく、イミュータブル設計が状態管理と好相性 |
| 非同期処理が多いバックエンド | retry, parallel, tryit などが標準装備 |
| バンドルサイズを極限まで削りたい | ESMネイティブで完璧なTree Shaking |
lodash を選ぶべきケース
| ユースケース | 理由 |
|---|---|
| 既存の大規模プロジェクトの保守 | 移行コストに見合わない場合が多い |
| JavaScriptプロジェクト(型なし) | radash の最大の利点(型安全性)が活きにくい |
| ニッチなユーティリティが必要 | _.cloneDeep, _.merge, _.template など radash にない関数が必要な場合 |
| チームの学習コストを最小化したい | 日本語情報が豊富で、知っているメンバーが多い |
_.chain スタイルのパイプラインを多用 | radash にはチェーンAPIがない |
どちらでもよいケース
- 小規模なスクリプトやCLIツールで、使う関数が数個程度の場合はどちらを選んでも差は出ません。
- そもそもユーティリティライブラリが不要な場合もあります。 ES2024+ の
Object.groupBy、Array.prototype.toSortedなどネイティブAPIの充実により、ライブラリなしで済むケースが増えています。
6. まとめ
lodash = 歴史と網羅性の「百科事典」
radash = 型安全とモダン設計の「厳選ツールキット」
lodash は10年以上にわたりJavaScriptエコシステムを支えてきた偉大なライブラリです。しかし、TypeScriptの普及・ESMの標準化・バンドルサイズへの意識の高まりという時代の変化の中で、radash はそれらの課題を最初から解決する設計で登場しました。
迷ったら、まず radash を試してみてください。 足りない関数があれば lodash から個別インポートで補完する、というハイブリッド戦略も現実的です。どちらも MIT ライセンスの優れたOSSであり、プロジェクトの要件に合わせて柔軟に選択することが最善のアプローチです。