Kuinka voit muuntaa potentiaalisesti vain-luku -avaimen luettavaksi ja kirjoitettavaksi avaimeksi TypeScriptissä?


Leikkikenttä

TypeScriptissä, kuinka voit "varmistaa", että tietyn tyypin keyof ei ole readonly, kun se teknisesti voi olla?

class Foo {
  x = 1
  readonly y = 2
}

const foo = new Foo()

const k = 'x' as keyof Foo

foo[k] = 3

Tässä tapauksessa tiedän varmasti, että avain ei ole vain luku -tilassa, mutta 'any'-tyyppiin muuntaminen ei ratkaise sitä. Ainoa ratkaisuni tähän mennessä on muuntaminen (foo as any)[k]=3, mutta varmasti löytyy parempi tapa?

Vastaus #1

TypeScript sisältää Readonly-utility-tyypin, joka on suunniteltu merkitsemään kaikki Record-avaimet readonly-tilaan. Vastakkaisen tavoitteen saavuttamiseksi voimme käyttää seuraavaa syntaksia:

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

Sitten voimme tyyppiä foo käyttäen:

class Foo {
  x = 1
  readonly y = 2
}

const foo = new Foo()

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

Vaihtoehtoisesti, jos haluat vain saada kirjoitettavia avaimia tietueelle:

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

Vastaus #2

Sinun täytyy kaventaa avaimesi oikeisiin kirjoitettaviin avaimiin tai tehdä vain-lukuominaisuuksista sellaisia, että ne tukevat ajonaikaista tarkistusta:

Leikkikenttä

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!');
}

Aiheeseen liittyviä artikkeleita