Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

248
Vistas
Reflection-like conversion of key to a string in TypeScript?

I use a Proxy object with the idea that whenever a property gets updated, some other side-effect can also be initiated. I don't want to initiate the side effects in the many places that properties get set (DRY principle).

Somewhat contrived example code:

const session = new Proxy(
  { id: undefined as number, age: undefined as number}, // =target
  {
    set: (target, property, value): boolean => {
      switch (property) {
          case 'id': {
            target[property] = value;
            this.notifyIdWasUpdated(value);
            return true;
          }
          case 'age': {
            target[property] = value;
            this.updateAgeDisplay(value);
            return true;
          }
          default: {
            return false;
          }
        }
    }
  }
);

My problem is that when I use my IDE's refactoring to change a property name (key) of the target object (e.g. age), the string constants in the case statements (e.g. 'age') don't get updated as well (potentially causing a bug).

Question: Is there a way to dynamically get a string value 'key' from an expression obj.key in the case statement (which would then be refactoring proof)? (Sort of the inverse of the ['key'] accessor, in a way...) Alternatively, can you suggest another way to structure the above code to guard against this sort of programmer oversight?


  • I have found Get object property name as a string, but wonder if there is a less "iffy" solution - IMHO the tradeoff between a potential problem and adding a lot of code to guard against it is not worth it. (Many techniques seem to iterate through all keys and match on either property type or value; these will not be safe enough.)
  • Typescript's documentation seems to say that metadata emission for reflection-like use is not yet officially adopted. Also not worth it IMHO to add a whole experimental library just for this.
about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

You can try to use keyof here.

interface Session {
  id: number
  age: number
}

const session1 = new Proxy(
  { id: 0, age: 0 } as Session,
  {
    set: (target, property: keyof Session, value): boolean => {
      switch (property) {
        case 'id': {
          target[property] = value;
          this.notifyIdWasUpdated(value);
          return true;
        }
        case 'age': {
          target[property] = value;
          this.updateAgeDisplay(value);
          return true;
        }
        default: {
          return false;
        }
      }
    }
  }
);

This will not be renamed automatically, but typescript will show error if property in case doesn't exist in Session.

The following case should allow automatic rename:

interface Session {
  id: number
  age: number
}

type Handlers<Model> = {
  [Key in keyof Model]: (newValue: Model[Key]) => void;
}

// Partial<Handlers<Session>> in case you don't want to handle each property
const handlers: Handlers<Session> = {
  id: () => { },
  age: () => { },
}

const session = new Proxy(
  { id: 0, age: 0 } as Session,
  {
    set: (target, property: keyof Session, value): boolean => {
      const handler = handlers[property];

      if (handler) {
        handler(value)

        return true;
      }

      return false;
    }
  }
);

about 4 years ago · Juan Pablo Isaza Denunciar

0

Although another answer was chosen, the problem with the given sample code is on a somewhat higher level of abstraction. Since the session object encapsulates a number of properties, the session object should be handled as a unit and not the properties separately. (There is probably a code smell name or some other warning against this...)

The sample would then simply be:

session = new Proxy(
  { id: undefined, age: undefined}, // =target
  {
    set: (target, property, value): boolean => {
        if (typeof property === 'string' && Object.keys(target).includes(<string>property)) {
          target[property] = value;
          doSideEffects(target);
          return true;
        } else {
          return false;
        }
      },
    }
  );

This simplifies the handler in the Proxy.

(I'm the OP. In my case, it has now also simplified the side effect code considerably. I guess the rubber duck effect came into play...)

about 4 years ago · Juan Pablo Isaza Denunciar

0

The simples solution would be something like this:

function nameof<TType>(selector: (t?: TType) => any) {
  const match = selector
    .toString()
    .match(/=>\s*(?:[a-zA-Z0-9_$]+\.?)*?([a-zA-Z0-9_$]+)$/);

  if (match) {
    return match[1];
  }

  return undefined;
}

interface MyType {
  id: any;
  age: number;
}

const session = new Proxy(
  { id: undefined as number, age: undefined as number }, // =target
  {
    set: (target, property, value): boolean => {
      switch (property) {
        case nameof<MyType>((t) => t.id): {
          target[property] = value;
          this.notifyIdWasUpdated(value);
          return true;
        }
        case nameof<MyType>((t) => t.age): {
          target[property] = value;
          this.updateAgeDisplay(value);
          return true;
        }
        default: {
          return false;
        }
      }
    },
  }
);

DEMO

NOTE: Careful, if you target ES5! Arrow function is transpiled into regular function with return so that regex will not work, you have to change the regex.

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda