typescript - Jeg vil ha et helt uforanderlig objekt i TS


Jeg har noen store gjenstander, som

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

Og jeg vil at TS ikke skal tillate meg å gjøre

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

Hvordan kan jeg immutere objektet helt? Er det bare ved å lage et grensesnitt med "readonly" og grensesnitt for hvert dypt nestet objekt?

Svar #1

Vi har nå alternativet as const, som er en syntaktisk kortfattet måte å gjøre det på som @phil294 nevnte som det første alternativet (nestet readonly).

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

a.d.e.f.h.boom = 'respek'; //Cannot assign to 'boom' because it is a read-only property.ts(2540)

Som en ekstra bonus kan du gjøre innspill til funksjoner som nestede uforanderlige ved å bruke dette trikset:

type ImmutableObject<T> = {
  readonly [K in keyof T]: Immutable<T[K]>;
}

export type Immutable<T> = {
  readonly [K in keyof T]: T[K] extends Function ? T[K] : ImmutableObject<T[K]>;
}

så dette ville skje

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

function mutateImmutable(input: Immutable<typeof a>) {
    input.d.e.f.h.boom = 'respek'; //Cannot assign to 'boom' because it is a read-only property.ts(2540)
}

Svar #2

Som beskrevet på https://www.typescriptlang.org/docs/handbook/interfaces.html, kan du bruke readonly på klasse-/grensesnittegenskaper eller Readonly<...>/ReadonlyArray<> for uforanderlige objekter og arrays. I ditt tilfelle vil dette se slik ut:

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.

Åpenbart er dette en ganske tautologisk uttalelse, så jeg foreslår at du definerer riktig klasserstruktur for objektet ditt. Du utnytter egentlig ingen av Typescripts funksjoner bare ved å erklære et nestet, utypet objekt.

Men hvis du virkelig trenger å gå uten typedefinisjoner, tror jeg den eneste måten er å definere en fryser (elsker begrepet :D) som Hampus foreslo. Hentet fra deepFreeze(obj)-funksjonen fra 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: Du kan ikke få kompilator typefeil uten å definere typer. Det er hele poenget med Typescript.

rediger :

den aller siste uttalelsen er feil. For eksempel,

let a = 1
a = "hello"

vil kaste en feil fordi typen er implisitt satt til nummer. For readonly derimot, tror jeg du vil trenge en korrekt deklarasjon som definert ovenfor.

Relaterte artikler