Kako u TypeScriptu pretvoriti potencijalno samo za čitanje ključ u ključ koji se može čitati i pisati?


Igralište

U TypeScriptu, kako možete 'osigurati' da keyof tipa nije samo za čitanje, kada tehnički moguće jest da jest?

class Foo {
  x = 1
  readonly y = 2
}

const foo = new Foo()

const k = 'x' as keyof Foo

foo[k] = 3

U ovom slučaju znam sigurno da ključ nije samo za čitanje, ali prebacivanje na "any" to ne rješava. Jedino rješenje koje do sada imam je prebacivanje (foo as any)[k]=3, ali sigurno postoji bolji način?

Odgovor #1

TypeScript ima Readonly pomoćni tip, koji je dizajniran za označavanje svih ključeva Record kao samo za čitanje. Da bismo postigli suprotno, možemo koristiti sljedeću sintaksu:

type Writeable<T> = { -readonly [K in keyof T]: T[K] }

Zatim možemo konvertirati naš foo tip:

class Foo {
  x = 1
  readonly y = 2
}

const foo = new Foo()

const k = 'x'
(foo as Writeable<Foo>)[k] = 3;

Alternativno, ako želite dobiti samo zapisive ključeve za zapis:

type EqualMutability<A, B> = (<T>() => T extends A ? true : false) extends
  (<T>() => T extends B ? true : false) ? true : false

type IsReadonly<R, K extends keyof R> = EqualMutability<
  { readonly [S in K]: R[K] },
  { [S in K]: R[K] }
>

type PickWritable<R> = Pick<R, { [K in keyof R]: IsReadonly<R, K> extends false ? K : never }[keyof R]>

const k: keyof PickWritable<Foo> = "x"
foo[k] = 3

Odgovor #2

Treba suziti ključeve na odgovarajuće zapisive ključeve ili omogućiti svojstvima samo za čitanje podršku za provjeru u vrijeme izvođenja:

Igralište

class Foo {
  x = 1
  get y(){ return 2;}
}

const foo = new Foo()

const k = 'x' as keyof Foo


// option 1
if(k === 'x'){ 
    foo[k] = 3
}

// option 2 (you need to use getters)
const canWrite = <T extends object, K extends keyof T>(obj: T, key: K): obj is T & {-readonly[k in K]: T[k]} => {
    const desc = Object.getOwnPropertyDescriptor(obj, key) ?? {};
    if(desc.get && !desc.set) return false;
    if(!desc.set && !desc.writable) return false;
    return true;
}

if(canWrite(foo, k)){
    console.log('can write!');
    foo[k] = 3;
}

if(!canWrite(foo, 'y')){
    console.log('cannot write!');
}

Povezani članci