just-safe-set
Version:
set value at property, create intermediate properties if necessary
64 lines (56 loc) • 1.48 kB
JavaScript
var objectSafeSet = set;
/*
var obj1 = {};
set(obj1, 'a.aa.aaa', 4); // true
obj1; // {a: {aa: {aaa: 4}}}
var obj2 = {};
set(obj2, ['a', 'aa', 'aaa'], 4); // true
obj2; // {a: {aa: {aaa: 4}}}
var obj3 = {a: {aa: {aaa: 2}}};
set(obj3, 'a.aa.aaa', 3); // true
obj3; // {a: {aa: {aaa: 3}}}
const obj5 = {a: {}};
const sym = Symbol();
set(obj5.a, sym, 7); // true
obj5; // {a: {Symbol(): 7}}
*/
function set(obj, propsArg, value) {
var props, lastProp;
if (Array.isArray(propsArg)) {
props = propsArg.slice(0);
}
if (typeof propsArg == 'string') {
props = propsArg.split('.');
}
if (typeof propsArg == 'symbol') {
props = [propsArg];
}
if (!Array.isArray(props)) {
throw new Error('props arg must be an array, a string or a symbol');
}
lastProp = props.pop();
if (!lastProp) {
return false;
}
prototypeCheck(lastProp);
var thisProp;
while ((thisProp = props.shift())) {
prototypeCheck(thisProp);
if (typeof obj[thisProp] == 'undefined') {
obj[thisProp] = {};
}
obj = obj[thisProp];
if (!obj || typeof obj != 'object') {
return false;
}
}
obj[lastProp] = value;
return true;
}
function prototypeCheck(prop) {
// coercion is intentional to catch prop values like `['__proto__']`
if (prop == '__proto__' || prop == 'constructor' || prop == 'prototype') {
throw new Error('setting of prototype values not supported');
}
}
export {objectSafeSet as default};