🛋️

TypeScriptのReadonly<T>を知ろう

に公開

概要

別記事を書くにあたりReadonly<T>を理解しようの会

Readonly<T>とは

ユーティリティ型の1つ。

型Tのプロパティにreadonlyを付けた状態で返す時に使う。
参考: https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype

type Hoge = string[];
type Fuga = [string, number, boolean];
type Piyo = { name: string };

const hoge: Readonly<Hoge> = ["ほげ", "ふが"]; // readonly string[]
const fuga: Readonly<Fuga> = ["ほげ", 20, true]; // readonly [string, number, boolean]
const piyo: Readonly<Piyo> = { name: "ほげ" }; // { readonly name: string }

hoge.push("ぴよ"); // 🚫readonly string[] なのでNG
fuga.push("ぴよ"); // 🚫readonly [string, number, boolean] なのでNG
piyo.name = "ぴよ"; // 🚫{ readonly name: string }なのでNG

readonlyを付与できない型を対応しようとしてもエラーにはならないがreadonly特有の動きとかは特にない。

type Hoge = string;

const hoge: Readonly<Hoge> = "ほげ"; // ✅エラーにはならないが型はstringになる

ちなみに

ネストされたオブジェクトにはreadonlyが付かない。

type Hoge = {
  name: "ほげ";
  birthday: { year: number; month: number; day: number };
};

const hoge: Readonly<Hoge> = {
  name: "ほげ",
  birthday: { year: 2000, month: 1, day: 1 },
}; // { readonly name: "ほげ"; readonly birthday: { year: 2000; month: 1; day: 1} }

hoge.name = "ふが"; // 🚫nameはreadonlyなのでNG
hoge.birthday.year = 2001; // ✅birthday.yearはreadonlyじゃないのでOK

なのでプロパティ全てをreadonlyにしたい場合は Readonly<T> は使わず、
以下のように as const をして型定義してあげるとOK。

const object = {
  name: "ほげ",
  birthday: { year: 2000, month: 1, day: 1 },
} as const;

type Hoge = typeof object; // { readonly name: "ほげ"; readonly birthday: { readonly year: 2000; readonly month: 1; readonly day: 1} } 

const hoge: Hoge = {
  name: "ほげ",
  birthday: { year: 2000, month: 1, day: 1 },
};

hoge.name = "ふが"; // 🚫nameはreadonlyなのでNG
hoge.birthday.year = 2001; // 🚫birthday.yearはreadonlyなのでNG

Discussion