Interceptação e Validação de Propriedades com Proxy no JavaScript

O objeto Proxy introduzido no ES6 permite criar uma camada de interceptação para operações fundamentais de um objeto, como leitura, escrita, verificação de existência e exclusão de propriedades. Ele é instanciado recebendo um objeto alvo (target) e um manipulador (handler) que define as regras de interceptação.

A sintaxe básica para a criação de um prroxy é a seguinte:

const alvo = {};
const manipulador = {};
const proxy = new Proxy(alvo, manipulador);

Abaixo, demonstramos como utilizar as enterceptações (traps) get, set, has e deleteProperty para controlar o acesso e a modificação de um objeto de configuração, utilizando o objeto Reflect para garantir o comportamento padrão quando necessário.

const configAlvo = {
  host: 'localhost',
  porta: 8080,
  chaveSecreta: 'abc123'
};

const configProxy = new Proxy(configAlvo, {
  get(alvo, propriedade) {
    console.log(`[GET] Acessando propriedade: ${propriedade}`);
    return Reflect.get(alvo, propriedade);
  },
  set(alvo, propriedade, valor) {
    console.log(`[SET] Modificando ${propriedade} para ${valor}`);
    if (propriedade === 'porta' && (typeof valor !== 'number' || valor <= 0)) {
      throw new Error('Porta inválida');
    }
    return Reflect.set(alvo, propriedade, valor);
  },
  has(alvo, propriedade) {
    console.log(`[HAS] Verificando existência de: ${propriedade}`);
    if (propriedade === 'chaveSecreta') return false;
    return Reflect.has(alvo, propriedade);
  },
  deleteProperty(alvo, propriedade) {
    console.log(`[DELETE] Tentativa de remover: ${propriedade}`);
    if (propriedade === 'host') {
      throw new Error('Não é permitido remover o host');
    }
    return Reflect.deleteProperty(alvo, propriedade);
  }
});

console.log(configProxy.host);
configProxy.porta = 3000;
console.log('porta' in configProxy, 'chaveSecreta' in configProxy);
delete configProxy.porta;
console.log(configProxy);

Ao executar as operações acima, os logs demonstram como cada ação foi interceptada. A propriedade chaveSecreta é ocultada na verificação com o operador in, e a tentativa de remover o host geraria uma exceção.

Além do controle básico de acesso, o Proxy é amplamente utilizado para validação de dados. Podemos combinar Proxy com Reflect para garentir que um objeto respeite regras de tipagem e domínio antes de aceitar uma mutação, atuando como um esquema de validação em tempo de execução.

const aplicarValidacao = (entidade, regras) => {
  return new Proxy(entidade, {
    set(alvo, propriedade, valor, receptor) {
      if (!Object.prototype.hasOwnProperty.call(alvo, propriedade)) {
        throw new Error(`A propriedade '${propriedade}' não está definida no esquema.`);
      }

      if (regras[propriedade] && !regras[propriedade](valor)) {
        throw new Error(`Valor inválido para '${propriedade}': ${valor}`);
      }

      return Reflect.set(alvo, propriedade, valor, receptor);
    }
  });
};

const regrasInventario = {
  sku: (val) => typeof val === 'string' && val.length > 0,
  quantidade: (val) => Number.isInteger(val) && val >= 0,
  preco: (val) => typeof val === 'number' && val > 0
};

class ItemInventario {
  constructor(sku, quantidade, preco) {
    this.sku = sku;
    this.quantidade = quantidade;
    this.preco = preco;

    return aplicarValidacao(this, regrasInventario);
  }
}

const item = new ItemInventario('ITEM-001', 50, 19.99);
console.log(item);

// item.quantidade = -5;  // Lança Error: Valor inválido para 'quantidade': -5
// item.sku = 123;        // Lança Error: Valor inválido para 'sku': 123
// item.categoria = 'X';  // Lança Error: A propriedade 'categoria' não está definida no esquema.

Neste cenário de validação, o construtor da classe retorna o proxy configurado. Qualquer tentativa de atribuir tipos incorretos, valores fora do domínio esperado ou propriedades não mapeadas lançará exceções imediatas, garantindo a integridade e a consistência dos dados da entidade.

Tags: javascript ES6 proxy reflect metaprogramming

Publicado em 7-9 05:21