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