zukucode
主にWEB関連の情報を技術メモとして発信しています。

TypeScript readonly配列で意図しない変更を防ぐ

関数へ配列を渡すと、その関数からpushsortで元の配列を書き換えられます。読み取りだけをする関数では、引数をreadonlyにして意図を型で表します。

読み取り専用の引数にする

const total = (prices: readonly number[]): number =>
  prices.reduce((sum, price) => sum + price, 0);

const prices = [100, 200, 300];
console.log(total(prices)); // 600

totalの中でprices.push(400)を書くと型エラーになります。呼び出し元の配列は通常の配列でも渡せます。

逆に、関数の引数を変更可能な number[] にすると、as constで定義した値を渡せません。読み取りしか行わない関数なら、最初から readonly number[] を受け取る方が利用できる配列の範囲も広がります。

並び替えたいときはコピーする

const sortedPrices = (prices: readonly number[]): number[] =>
  [...prices].sort((a, b) => a - b);

sortは対象を変更するため、先にスプレッド構文でコピーします。

const source = [300, 100, 200];
const sorted = sortedPrices(source);
console.log(source); // [300, 100, 200]
console.log(sorted); // [100, 200, 300]

コピーが必要なのは、sortなど元の配列を書き換えるメソッドを使うときです。mapfilterは新しい配列を返します。

どこまで守られるか

readonlyはTypeScriptの型チェックであり、実行時に配列を凍結しません。また、配列内のオブジェクトまで深く読み取り専用にするものでもありません。外部入力の改ざん防止には別の仕組みが必要です。

const products: readonly { name: string }[] = [{ name: 'ノート' }];
products[0].name = 'ペン'; // 要素内のプロパティは変更できる

関連記事

  • TypeScript unknown型を安全に確認して使用する

    unknownは、どのような値が入っているか分からないことを表す型です。外部から受け取ったJSONやcatchした値を、確認せず操作したくない場合に使います。anyは型チェックを無効にしますが、unk...


  • TypeScript Union型を条件分岐で安全に使い分ける

    APIの結果が成功または失敗のどちらかになる場合、両方の項目を任意にした1つの型へ詰め込むと、存在しない値を読みやすくなります。Union型なら、あり得る状態を別々に表せます。statusはどの型かを...


  • TypeScript typeとinterfaceの基本的な使い分け

    TypeScriptでは、typeとinterfaceのどちらでもオブジェクトの形を定義できます。似ている部分が多いため、最初はどちらを使えばよいか迷うことがあります。今回は基本的な書き方と、分かりや...


  • TypeScript Problem DetailsをZodで検証してAPIエラーを扱う

    fetchは、サーバーが400や500を返してもPromiseをrejectしません。そのため、HTTPステータスを確認し、レスポンスの形式を検証してから、画面で扱いやすいエラーへ変換する必要がありま...


  • TypeScript オプショナルチェーンとNull合体演算子の使い方

    APIから受け取るデータには、値が存在しない項目が含まれることがあります。TypeScriptでは、オプショナルチェーンの?.とNull合体演算子の??を使用すると、nullやundefinedを安全...


  • TypeScript async awaitで非同期処理の失敗を扱う

    APIの結果を待ってから画面に表示したいときは、asyncとawaitを使います。ただし、fetchはHTTPの404や500だけでは例外になりません。通信失敗とHTTPエラーを分けて扱います。awa...


  • TypeScript mapとfilterで配列を変換する

    mapとfilterは、配列から新しい配列を作るメソッドです。どちらも元の配列を変更しませんが、役割は異なります。要素数を保ったまま各値を変換するならmap、条件に合う要素だけへ減らすならfilter...


  • AjvでAPIのレスポンスの型チェックを行う

    TypeScript fetchの共通処理を実装するでfetchの共通処理を作成しましたが、これはAPIからのレスポンスの形式があっている前提で処理を行っています。例えば以下はAPIのレスポンスの型は...


  • React useEffectで無限ループが発生するときに確認すること

    ReactのuseEffectを利用したときに無限ループが発生してしまうことがあります。特に注意したいのが、ESLintのreact-hooks/exhaustive-depsで表示された警告をUpd...


  • React 関数コンポーネントにプロパティを設定する

    React+TypeScriptで作成した関数コンポーネントにプロパティを設定する方法を紹介します。以下は送信ボタンのコンポーネントを作成しています。isSubmittingというプロパティを用意して...