const Ajv = require('ajv') module.exports = (obj={}, inSchema, outSchema) => { if (!inSchema) throw new ReferenceError('input schema not defined') const inValidate = (new Ajv()).compile(inSchema) let outValidate; if (outSchema){ outSchema = {...outSchema} outSchema.additionalProperties = false outValidate = (new Ajv()).compile(outSchema) } return new Proxy(obj, { get(obj, prop){ if (outValidate && !outValidate(obj)){ if (outValidate.errors.find(({instancePath}) => instancePath.slice(1).startsWith(prop)) || (outValidate.errors[0].keyword === 'additionalProperties' && outValidate.errors[0].params.additionalProperty === prop)){ return; } } return obj[prop] }, set(obj, prop, value){ const newObject = {...obj, [prop]: value} if (!inValidate(newObject)){ throw new TypeError(JSON.stringify([inValidate.errors, prop, value], null, 4)) } obj[prop] = value } }) } const schema = { type: "object", properties: { foo: {type: "integer"}, bar: {type: "string"} }, required: ["foo"], additionalProperties: false } const outSchema = { type: "object", properties: { foo: {type: "integer"}, }, required: ["foo"], additionalProperties: false } let validated = module.exports({}, schema, outSchema) validated.foo = 5; validated.bar = "string"; console.log(validated.foo) console.log(validated.bar) console.log({...validated}) //validated.bar = 100500;