🍄

OxLintのパフォーマンス系ルールで「へ〜」ってなる

に公開

概要

OxLint のパフォーマンスカテゴリのルールを読んだら、今まであまり気にしていなかった書き方もあったのでまとめます。
ルールは 2026/02/08 時点のものを記載しています。

なお、個人的には実運用を考えると有効にしづらいルールも多い印象でしたので、絶対守っていくべき!とは思っていないです。
理解した上で無効にしたり、実装時にメリット/デメリットを踏まえて自分で選べるようになると良いと思いました。

OxLint とは

ESLint と比較して 50 〜 100 倍速いと公式が謳っているリンターです。
js, ts 系に対応しており、ESLint の主要なルールを移植しています。

OxLint では全てのルールにカテゴリが設定されており、カテゴリごとにルールの有効/無効を指定できます。
カテゴリ別に警告レベルを指定しておいて、個別のルールについてオーバーライド設定することで好みのルールセットにしていく使い方です。
参考: 公式 カテゴリ一覧

カテゴリ名 概要
correctness 明確に間違っているコード(バグの可能性が高い問題)を検出するカテゴリ
suspicious 間違っている可能性が高いコード(意図しない挙動・誤用の可能性)を検出するカテゴリ
pedantic より厳しめの指摘(細かな正しさ・一貫性・潜在的な問題)を検出するカテゴリ
perf パフォーマンス改善につながる指摘を検出するカテゴリ
style コーディングスタイル(整形・書き方の好み)に関する指摘のカテゴリ
restriction 構文や利用パターンの制約(特定の構文を禁止・制限する等)に関する指摘のカテゴリ
nursery 実験的で変更される可能性があるルール群のカテゴリ

Perf カテゴリ

パフォーマンス改善につながる指摘を検出するカテゴリです。
公式 ルール一覧CategoryPerf を指定すると確認できます。
記事作成時点で 12 とルールは少なめなので、1 つずつ完結にまとめていきます。
公式にはより詳細な説明や他の事例コードもあるので、気になった方は公式を参照してください。

react-perf/jsx-no-jsx-as-prop

JSX 要素を直接 props として渡してはいけない。
親コンポーネントがレンダリングされるたびに、JSX 要素の新しいインスタンスが作成されて子コンポーネントの不要な再レンダリングを引き起こす。

// ダメな例
<Item jsx={<SubItem />} />

react-perf/jsx-no-new-array-as-prop

配列を直接 props として渡してはいけない。
親コンポーネントがレンダリングされるたびに、配列の新しいインスタンスが作成されて子コンポーネントの不要な再レンダリングを引き起こす。

// ダメな例
<Item list={[]} />

react-perf/jsx-no-new-function-as-prop

関数を直接 props として渡してはいけない。
親コンポーネントがレンダリングされるたびに、関数の新しいインスタンスが作成されて子コンポーネントの不要な再レンダリングを引き起こす。

// ダメな例
<Item callback={new Function(...)} />

react-perf/jsx-no-new-object-as-prop

オブジェクトを直接 props として渡してはいけない。
親コンポーネントがレンダリングされるたびに、オブジェクトの新しいインスタンスが作成されて子コンポーネントの不要な再レンダリングを引き起こす。

// ダメな例
<Item config={{}} />

oxc/no-accumulating-spread

Array.prototype.reduce() や繰り返し処理内で、スプレッド構文 ({...obj}, [...array]) を使わない。
スプレッド構文はその場で新しいオブジェクトや配列を生成するので、回数が多いとコストが高くなる。

// ダメな例
arr.reduce((acc, x) => ({ ...acc, [x]: fn(x) }), {});

// 良い例
arr.reduce((acc, x) => acc.push(fn(x)), []);

react/no-array-index-key

要素の key に index を使わない。
配列がソートされたりすると同じ要素でも index が変わって無駄なレンダリングが発生する。

// ダメな例
things.map((thing, index) => <Hello key={index} />);

// 良い例
things.map((thing, index) => <Hello key={thing.id} />);

eslint/no-await-in-loop

繰り返し処理中に await を使わない。
非同期処理を並列化した方が良い。

// ダメな例
async function bad() {
  for (const user of users) {
    const userRecord = await getUserRecord(user);
  }
}

// 良い例
async function good() {
  await Promise.all(users.map((user) => getUserRecord(user)));
}

oxc/no-map-spread

map(), flatMap() の中でスプレッド構文 ([...array]) を使わない。
スプレッド構文はその場で新しい配列を生成するので、回数が多いとコストが高くなる。

// ダメな例
scores.map((score) => ({ ...score, rank: getRank(score) }));

// 良い例
scores.map((score) => Object.assign(score, { rank: getRank(score) }));

eslint/no-useless-call

不要な .call(), .apply() を使わない。
通常の関数呼び出しの方が速い。

// ダメな例
foo.call(undefined, 1, 2, 3);

// 良い例
foo(1, 2, 3);

unicorn/prefer-array-find

配列の最初の要素を取得したいなら、filter(...)[0] より find() を使う。
find() の方が最適化されており、最初の要素が見つかった段階で配列捜査を終了するので効率的。

// ダメな例
users.filter((u) => u.id === id)[0];

// 良い例
users.find((u) => u.id === id);

unicorn/prefer-array-flat-map

map().flat() より .flatMap() を使う。
flatMap() の方が処理が最適化されている。

// ダメな例
[1, 2, 3].map((i) => [i]).flat();

// 良い例
[1, 2, 3].flatMap((i) => [i]);

unicorn/prefer-set-has

Array#includes() より Set#has() を使う。
Set#has() の方が処理が速い。

(個人的補足) 要素数が少ない場合は Set を生成するコストの方が高くなることもある。

// ダメな例
const array = [1, 2, 3];
const hasValue = (value) => array.includes(value);

// 良い例
const set = new Set([1, 2, 3]);
const hasValue = (value) => set.has(value);

まとめ

以上、OxLint のパフォーマンスカテゴリに属するルールでした。

Discussion