Dynamic Object Property Access Patterns
Reading and writing object properties from a string key (or an array of keys) is one of the small skills that separates clean code from a sprawl of `if/else` chains. This snippet covers bracket access with `obj[key]`, safe nested reads with optional chaining and a path walker, and destructuring with computed and renamed keys. Use these patterns when keys come from config, query strings, or user input.
984 views
11
const person = { name: 'Ada', age: 36, role: 'engineer' };
function get(obj, key) {
return obj[key];
}
function set(obj, key, value) {
obj[key] = value;
return obj;
}
console.log(get(person, 'name')); // Ada
set(person, 'age', 37);
console.log(person.age); // 37
// Computed keys at construction time
const field = 'role';
const patch = { [field]: 'staff engineer' };
console.log(patch); // { role: 'staff engineer' }Dot access obj.name only works when the key is a fixed identifier known at write time. As soon as the key sits in a variable, bracket access obj[key] is the only option, and it accepts any string or symbol expression. The same [field] syntax works inside an object literal to compute the key at construction time, which removes a lot of obj[field] = value follow-ups. Pull these patterns out as tiny helpers when the same lookup is repeated, but inline them otherwise so the call site stays obvious.
const order = {
id: 'ord_42',
customer: { name: 'Grace', address: { city: 'NYC' } }
};
console.log(order?.customer?.address?.city); // NYC
console.log(order?.shipping?.address?.city); // undefined
console.log(order?.customer?.address?.zip ?? 'NA'); // NA
function getPath(obj, path) {
return path.split('.').reduce((acc, key) => (acc == null ? acc : acc[key]), obj);
}
console.log(getPath(order, 'customer.address.city')); // NYC
console.log(getPath(order, 'customer.address.zip')); // undefined
console.log(getPath(order, 'shipping.address.city')); // undefinedOptional chaining ?. short-circuits on null or undefined so you can read four levels deep without a stack of && guards. Pair it with the nullish coalescing ?? operator to fall back to a default only when the value is truly missing (zero, empty string, and false are kept). When the path itself is dynamic (loaded from config, etc.), a tiny getPath walker that splits on . and threads through reduce covers the same ground. The acc == null check stops early at both null and undefined, which is usually what you want for missing data.
const settings = { theme: 'dark', density: 'compact', locale: 'en-US' };
// Rename + default in one step
const { theme: uiTheme, fontSize = 14 } = settings;
console.log(uiTheme, fontSize); // dark 14
// Computed destructuring key
const keyName = 'locale';
const { [keyName]: activeLocale } = settings;
console.log(activeLocale); // en-US
// Pull out one field, keep the rest
const { theme, ...rest } = settings;
console.log(theme); // dark
console.log(rest); // { density: 'compact', locale: 'en-US' }Destructuring is not just shorthand for const x = obj.x. The key: alias form renames at the call site, key = default supplies a default when the property is undefined, and [expr]: alias reads a key whose name is computed at runtime. The rest pattern ...rest collects every property you did not name, which is the cleanest way to forward an options object after stripping one field. Combine these in one destructuring expression and a noisy chain of assignments collapses into one line.
