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