コピペで使える JavaScript / TypeScript 実践 Tips 集【中級者向け】
知っているようで知らない JavaScript / TypeScript の便利テクニックを、コピペして即使えるコード付きでまとめました。「もっと早く知りたかった」と思えるものを中心に厳選しています。
配列操作をスマートに書く
重複削除は Set で一発
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)];
// [1, 2, 3]
オブジェクトの配列で特定キーを軸に重複除去したい場合は Map を使います。
type User = { id: number; name: string };
const users: User[] = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 1, name: "Alice (dup)" },
];
const uniqueById = [...new Map(users.map((u) => [u.id, u])).values()];
// id: 1 は最後のエントリで上書きされる
groupBy でグルーピング(Object.groupBy)
ES2024 で標準化された Object.groupBy が便利です。polyfill なしで使えるランタイムが増えています。
const items = [
{ category: "fruit", name: "apple" },
{ category: "veggie", name: "carrot" },
{ category: "fruit", name: "banana" },
];
const grouped = Object.groupBy(items, (item) => item.category);
// { fruit: [...], veggie: [...] }
flatMap で map + flatten を一回で
const sentences = ["hello world", "foo bar baz"];
const words = sentences.flatMap((s) => s.split(" "));
// ["hello", "world", "foo", "bar", "baz"]
空要素を除外するフィルタリングにも使えます。
const maybeNumbers = ["1", "two", "3", "four"];
const numbers = maybeNumbers.flatMap((s) => {
const n = Number(s);
return Number.isNaN(n) ? [] : [n];
});
// [1, 3]
TypeScript の型テクニック
satisfies 演算子で型推論を維持したまま型チェック
const config = {
port: 3000,
host: "localhost",
debug: true,
} satisfies Record<string, string | number | boolean>;
// config.port の型は number(string | number | boolean ではない)
const doubled = config.port * 2; // OK
as キャストと違い、型チェックが効いたまま元の型推論が残ります。
Awaited で Promise の中身の型を取り出す
async function fetchUser() {
return { id: 1, name: "Alice" };
}
type User = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string }
外部ライブラリの非同期関数の戻り値型を使い回すときに重宝します。
discriminated union でフェイルセーフな状態管理
type AsyncState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: Error };
function render(state: AsyncState<string>) {
switch (state.status) {
case "success":
return state.data; // data にアクセスできる
case "error":
return state.error.message; // error にアクセスできる
default:
return null;
}
}
infer で型の一部を抽出する
type UnwrapArray<T> = T extends Array<infer Item> ? Item : T;
type A = UnwrapArray<string[]>; // string
type B = UnwrapArray<number>; // number(配列でなければそのまま)
非同期処理のパターン
Promise.allSettled で全件処理 + エラーを握りつぶさない
const results = await Promise.allSettled([
fetch("/api/users"),
fetch("/api/posts"),
fetch("/api/comments"),
]);
for (const result of results) {
if (result.status === "fulfilled") {
console.log("OK", result.value);
} else {
console.error("NG", result.reason);
}
}
Promise.all と違い、1 件失敗しても残りの結果を捨てません。
AbortController でリクエストをキャンセル
function fetchWithTimeout(url: string, ms: number) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), ms);
return fetch(url, { signal: controller.signal }).finally(() =>
clearTimeout(timer)
);
}
React の useEffect クリーンアップや、古いリクエストのキャンセルにも同じパターンが使えます。
async/await のエラーを Result 型でラップする
毎回 try/catch を書くのが辛い場合に便利なユーティリティです。
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
async function tryCatch<T>(
promise: Promise<T>
): Promise<Result<T>> {
try {
return { ok: true, value: await promise };
} catch (e) {
return { ok: false, error: e instanceof Error ? e : new Error(String(e)) };
}
}
// 使い方
const result = await tryCatch(fetch("/api/data"));
if (!result.ok) {
console.error(result.error.message);
return;
}
const data = result.value; // Response
オブジェクト操作
Object.fromEntries でオブジェクトを変換する
Object.entries と組み合わせると、オブジェクトを変換する処理が簡潔に書けます。
const prices = { apple: 100, banana: 200, cherry: 300 };
// 全値を2倍にする
const doubled = Object.fromEntries(
Object.entries(prices).map(([k, v]) => [k, v * 2])
);
// { apple: 200, banana: 400, cherry: 600 }
// 特定キーだけ除外する
const { apple: _, ...withoutApple } = prices;
// { banana: 200, cherry: 300 }
構造化代入のデフォルト値と別名
function setup({ host = "localhost", port = 3000, debug: isDebug = false } = {}) {
console.log(host, port, isDebug);
}
setup({ port: 8080 });
// "localhost" 8080 false
小さいけど効く小技
?? と ?. を組み合わせてネストを安全に掘る
const user = null;
const city = user?.address?.city ?? "不明";
// "不明"
ラベル付き break でネストしたループを抜ける
outer: for (const row of matrix) {
for (const cell of row) {
if (cell === target) {
console.log("見つかった");
break outer;
}
}
}
structuredClone でディープコピー
const original = { a: { b: { c: 42 } } };
const clone = structuredClone(original);
clone.a.b.c = 99;
console.log(original.a.b.c); // 42(影響なし)
Date・Map・Set・ArrayBuffer なども正しくコピーされます(関数・DOM ノードは不可)。
crypto.randomUUID で UUID 生成
const id = crypto.randomUUID();
// "550e8400-e29b-41d4-a716-446655440000" のような文字列
ブラウザ・Node.js 18 以降・Deno で動作します。ライブラリ不要です。
まとめ
今回紹介した Tips をカテゴリ別に振り返ります。
| カテゴリ | Tips |
|---|---|
| 配列操作 | Set で重複除去、Map でオブジェクト重複除去、flatMap で map+filter |
| 型テクニック | satisfies、Awaited、discriminated union、infer |
| 非同期 | allSettled、AbortController、Result 型ラッパー |
| オブジェクト | Object.fromEntries、構造化代入のデフォルト値 |
| 小技 | ?? / ?.、ラベル付き break、structuredClone、crypto.randomUUID |
「知ってた!」という Tips も、実際にコードで見ると使い方のイメージが広がるはずです。ぜひ手元のコードに取り入れてみてください。