web-dev-qa-db-ja.com

TSで完全に不変のオブジェクトが欲しい

私はいくつかの大きなオブジェクトを持っています

const a={
 b:33, 
 c:[78, 99], 
 d:{e:{f:{g:true, h:{boom:'selecta'}}}};/// well, even deeper than this...

そして、私はTS notができるようにしたいと思います

a.d.e.f.h.boom='respek';

オブジェクトを完全に不変にするにはどうすればよいですか? 「読み取り専用」のインターフェイスと、深くネストされた各オブジェクトのインターフェイスを作成するだけですか?

9
shal

https://www.typescriptlang.org/docs/handbook/interfaces.html で説明されているように、クラス/インターフェイスプロパティでreadonlyを使用するか、_Readonly<...>_/_ReadonlyArray<>_不変のオブジェクトと配列の場合。あなたの場合、これは次のようになります。

_const a: Readonly<{
    b: number,
    c: ReadonlyArray<number>,
    d: Readonly<{
        e: Readonly<{
            f: Readonly<{
                g: boolean,
                h: Readonly<{
                    boom: string
                }>
            }>
        }>
    }>
}> = {
        b: 33,
        c: [78, 99],
        d:{e:{f:{g:true, h:{boom:'selecta'}}}}
}

a.d.e.f.h.boom = 'respek'; // error: Cannot assign to 'boom' because it is a constant or a read-only property.
_

明らかに、これはかなりトートロジー的なステートメントなので、オブジェクトに適切なクラス構造を定義することをお勧めします。ネストされた型なしオブジェクトを宣言するだけでは、TypeScriptの機能を実際に利用しているわけではありません。

しかし、本当に型の定義なしで行く必要がある場合、Hampusが提案したように冷凍庫(用語:Dが大好き)を定義するのが唯一の方法だと思います。 deepFreeze(obj) function MDNから から取得:

_function freezer(obj) {
    Object.getOwnPropertyNames(obj).forEach(name => {
        if (typeof obj[name] == 'object' && obj[name] !== null)
            freezer(obj[name]);
    });
    return Object.freeze(obj);
}

const a = freezer({
    b:33, 
    c:[78, 99], 
    d:{e:{f:{g:true, h:{boom:'selecta'}}}}});

a.d.e.f.h.boom='respek'; // this does NOT throw an error. it simply does not override the value.
_

tl; dr:型を定義せずにコンパイラ型エラーをできません。それがTypeScriptの要点です。

編集:

この最後のステートメントは間違っています。例えば、

_let a = 1
a = "hello"
_

タイプが暗黙的に数値に設定されているため、エラーがスローされます。ただし、読み取り専用の場合は、上記で定義した適切な宣言が必要になると思います。

2
Blauhirn

Minko Gechev はDeepReadonlyタイプを作成しました:

type DeepReadonly<T> =
  T extends (infer R)[] ? DeepReadonlyArray<R> :
  T extends Function ? T :
  T extends object ? DeepReadonlyObject<T> :
  T;

interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}

type DeepReadonlyObject<T> = {
  readonly [P in keyof T]: DeepReadonly<T[P]>;
};

interface Person {
  name: string;
  job: { company: string, position:string };
}

const person: DeepReadonly<Person> = {
  name: 'Minko',
  job: {
    company: 'Google',
    position: 'Software engineer'
  }
};

person.job.company = 'Alphabet'; // Error
0
Martin Jaskulla

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze または https://developer.mozilla)をご覧ください。 .org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.freezeはあなたが望むことをするかもしれませんが、少なくともWebStormはオブジェクトを編集しようとしても警告を出さず、Chromeはサイレントに失敗します。

const obj = { val: 1 }; 
Object.freeze(obj);
obj.val = 2;

console.log(obj);
-> { val: 1 }

凍結されたオブジェクトのプロパティセットに追加したり、プロパティセットから削除したりすることはできません。そうしようとすると、サイレントに、またはTypeError例外をスローすることによって失敗します。

0
Hampus