TypeScript の readonly と Readonly の違いを理解する

TwitterFacebookHatena
  • 公開:2023-07-23
  • 更新:2023-10-26
  • 文章量:2858
  • TypeScript

TL;DR

このページでは、TypeScript の readonly の特性について解説しますね。一言でいうと、readonly は TypeScript の特性で、特定のプロパティや変数が変更されないことを保証します。

開発環境 バージョン
Next.js 13.4.4
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

readonly とは?

readonly は TypeScript の特性で、それが付けられたプロパティや変数が再代入できないことを保証します。これにより、不意のデータの変更を防ぎ、バグを未然に防ぐことができます。

基本的な使用方法は次のようになります。

type ReadonlyUser = {
  readonly name: string
  readonly age: number
}

const user: ReadonlyUser = {
  name: 'Alice',
  age: 20,
}

// エラー!name は readonly なので変更できません
user.name = 'Bob'

このコードでは、ReadonlyUser 型に readonly を付けています。その結果、usernameage を変更することはできません。

この特性は、ある値が一度設定されると変更されないことを明示的に示すのに役立ちます。これにより、他の開発者がそのコードを読んだときに、その値が変更されることなく、安心してその値を使うことができます。

readonly と配列

readonly は配列にも使用できます。これにより、配列自体やその要素が変更されないことを保証できます。詳細は次の例をご覧ください。

const numbers: readonly number[] = [1, 2, 3]

// エラー!numbers は readonly なので変更できません
numbers.push(4)

// エラー!numbers[0] は readonly なので変更できません
numbers[0] = 10

このコードでは、numbersreadonly number[] 型として宣言されています。そのため、numbers 自体を変更したり、その要素を変更したりすることはできません。

この特性は、特に関数の引数として配列を受け取るときに有用です。関数の内部で配列を変更すると、その影響が関数の外部に波及する可能性があるため、配列が変更されないことを保証することで、バグの可能性を減らすことができます。

関数型コンポーネントでの Readonly

以下に、関数型コンポーネントでの Readonly 使用例を示します。この例では、コンポーネントが受け取るプロパティが変更されないように、Readonly を使用しています。

import React from 'react'

type Props = Readonly<{
  message: string
}>

const MessageComponent = ({ message }: Props) => {
  return <p>{message}</p>
}

上記の例では、MessageComponent コンポーネントは message プロパティを受け取り、それを表示します。ここで、Props の型を Readonly でラップすることで、受け取った message プロパティがコンポーネント内部で変更されないことを保証します。

このように、Readonly を使うことで、コードが安全になり、予期せぬ副作用を防ぐことができますね。

クラスのメンバに readonly を使用する

次に、クラスのメンバに readonly を使用する例を見てみましょう。

class User {
  readonly name: string
  readonly age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

const user = new User('Alice', 20)

// エラー!name は readonly なので変更できません
user.name = 'Bob'

ここでは、User クラスのメンバ nameagereadonly を指定しています。そのため、これらの値は初めて設定された後、変更することはできません。

このように、クラスのメンバに readonly を付けることで、そのメンバが一度設定された後は変更できないことを示すことができます。これは特に、その値がクラスの内部状態を表す場合や、一度設定されるとその後変更されるべきでない場合に有用です。

readonlyReadonlyの違い

TypeScript では、readonlyReadonlyは似ていますが、それぞれ異なる場面で使われます。

readonlyは、オブジェクトのプロパティや、クラスのメンバーを読み取り専用にします。これは、その値が一度設定されると、後から変更できないことを意味します。つまり、値の再代入が防がれます。

class ExampleClass {
  readonly name: string

  constructor(name: string) {
    this.name = name
  }
}

const instance = new ExampleClass('test')
instance.name = 'anotherTest' // Error! 'name' is a read-only property.

一方で、Readonlyは一種のユーティリティ型で、与えられた型の全てのプロパティをreadonlyにします。これは、オブジェクトのプロパティが再代入できないようにします。

type ReadonlyPerson = Readonly<{ name: string; age: number }>

const person: ReadonlyPerson = { name: 'John', age: 30 }

person.name = 'Jane' // Error! 'name' is a read-only property.

ここで、Readonlyユーティリティ型はオブジェクト全体を対象にするのに対して、readonly修飾子は個々のプロパティに対して使用される点が重要な違いです。

TypeScript の readonly と Readonly の違いを理解する