Motomichi Works Blog

モトミチワークスブログです。その日学習したことについて書いている日記みたいなものです。

TypeScript学習日記 その0013 readonly修飾子を解除する

参考にさせていただいたページ

サンプルコード

ただ値を複製して使いたい場合

ただ値を複製して使いたい場合は以下のようにスプレッド構文を使えば別のオブジェクトになりreadonlyが解除されますし、新しく作ったmutableExampleObjを変更する分にはreadonlyExampleObjに影響も無いので問題ありません。以下のサンプルコードのように as MutableType<ReadonlyExampleObjType> までするかどうかは実現したい要件次第だとは思います。この場合はそこまでしなくてもstring型であるこは推論されますし。

    type ReadonlyExampleObjType = {
      readonly hoge: string;
      readonly foo: string;
    };

    type MutableType<T> = {
      -readonly [Key in keyof T]: T[Key];
    };

    const readonlyExampleObj: ReadonlyExampleObjType = {
      hoge: 'hoge',
      foo:  'foo',
    } as const;

    const mutableExampleObj = { ...readonlyExampleObj } as MutableType<ReadonlyExampleObjType>;

    mutableExampleObj.hoge = 'newHoge'; // 上書きできる

どうしてもreadonlyなプロパティを書き換えたい場合

こんなことをしたくなる場合はそもそも設計がおかしいとは思いますが、まぁ事情は色々あると思いますし参考までに書いておきます。

    type ReadonlyExampleObjType = {
      readonly hoge: string;
      readonly foo: string;
    };

    type MutableType<T> = {
      -readonly [Key in keyof T]: T[Key];
    };

    const readonlyExampleObj: ReadonlyExampleObjType = {
      hoge: 'hoge',
      foo:  'foo',
    } as const;

    const mutableExampleObj = readonlyExampleObj as MutableType<ReadonlyExampleObjType>;

    readonlyExampleObj.hoge = 'newHoge'; // Cannot assign to 'hoge' because it is a read-only property.
    mutableExampleObj.hoge = 'newHoge'; // こっちは上書きできる

    // 当たり前ですが、readonlyHogeも書き変わるので注意!!
    console.log('readonlyExampleObj: ', readonlyExampleObj); // { hoge: 'newHoge', foo: 'foo' }

スプレッドした後に深いところのオブジェクトのプロパティはどうなるのか

あくまでスプレッド構文によってreadonlyが解除されるのは一階層目のプロパティだけなので、以下の場合bazはreadonlyのままです。

    type ReadonlyExampleObjType = {
      readonly hoge: string;
      readonly foo: {
        readonly baz: 'baz'
      };
    };

    const readonlyExampleObj: ReadonlyExampleObjType = {
      hoge: 'hoge',
      foo:  {
        baz: 'baz',
      },
    } as const;

    const mutableExampleObj = { ...readonlyExampleObj };

    mutableExampleObj.foo.baz = 'newBaz'; // Cannot assign to 'baz' because it is a read-only property.