Búsqueda de sitios web

¿Cómo conviertes una clave potencialmente de solo lectura en una clave de lectura y escritura en TypeScript?


Parque infantil

En TypeScript, ¿cómo se puede "afirmar" que un keyof de un tipo no es de solo lectura, cuando técnicamente podría serlo?

class Foo {
  x = 1
  readonly y = 2
}

const foo = new Foo()

const k = 'x' as keyof Foo

foo[k] = 3

En este caso, sé con certeza que la clave no es de solo lectura, pero convertir a "any" no lo soluciona. Mi única solución hasta ahora es convertir (foo as any)[k]=3, pero seguramente hay una mejor manera?

Respuesta #1

TypeScript cuenta con un tipo utilitario Readonly, que está diseñado para marcar todas las claves de Record como readonly. Para lograr lo inverso, podemos usar la siguiente sintaxis:

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

Entonces, podemos convertir nuestro tipo foo:

class Foo {
  x = 1
  readonly y = 2
}

const foo = new Foo()

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

Alternativamente, si solo quieres obtener claves escribibles para un registro:

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

Respuesta #2

Necesitas restringir tus claves a claves escribibles adecuadas o hacer que las propiedades de solo lectura admitan una verificación en tiempo de ejecución:

Patio de recreo

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

Artículos relacionados