Object restructuring syntax

There seems to be a common desire for a shorthand to create an object by picking properties off of another object. Here are a few proposals other users have brought up on this forum:

  • This proposal for syntax as follows: const { a, b } as newObj = oldObj
  • This proposed object literal shortand syntax: const newObj = { oldObj.a, oldObj.b }
  • One person proposed syntax for yielding only specific properties from an object.
  • A proposed Object.pick() function.

I'd like to add to this conversation by proposing an "object restructuring" syntax, that can provide this kind of desired behavior. This syntax is inspired by the Bosque programming language's Bulk Algebraic Operations and works as follows:

const user = {
  firstNamem: 'Spider',
  lastName: 'Man',
  city: 'New York',
  street: 'Walnut Street'
}

const address = user.{ city, street, state }
console.log(address) // { city: 'New York', street: 'Walnut Street', state: undefined }

We could additionally provide syntax similar to object destructuring, to allow renaming properties and providing default values. This fits well with syntax that javascript already has, but isn't a necessary part of this proposal.

const address = user.{ city: someCity, street = 'UNKNOWN', state = 'UNKNOWN' }
console.log(nameAttributes) // { someCity: 'New York', street: 'Walnut Street', state: 'UNKNOWN' }

Some example use cases for this syntax:

const createUser = opts => Object.freeze(
  opts.{ firstName, lastName, city, state, street, zipCode }
)
async function performQuery(params) {
  const { allowEmpty = true } = params
  const query = params.{ selectAttributes: select, whereCondition: where, tableName: table }
  const paging = params.{ limit = 0, offset: start = 0 }

  const entries = await database.query({ query, paging })
  if (entries.length === 0 && !allowEmpty) throw new Error('Query Failed!')
  return entries
}

Rational behind this syntax

There are a couple of alternative ways to achieve a similar idea in the language, but they don't scale well when there's a lot of properties being extracted.

Lets take the most straight-forwards approach:

const extractQueryParams = params => ({
  select: params.select,
  where: params.where,
  table: params.table,
  allowEmpty: params.allowEmpty,
  limit: params.limit,
  start: params.offset,
  ordering: params.ordering,
  orderBy: params.orderBy,
})

Did you notice that one of those parameters got renamed in this function? Its difficult to tell. Someone who wants to make it abundantly clear which properties are getting renamed, if any, might choose to destructure, then recreate the object from locals, like in this example:

async function performAnotherQuery(params) {
  const { select, where, table, allowEmpty, limit, offset, ordering, orderBy } = params
  const queryOptions = { select, where, table, limit: start, offset, ordering, orderBy }

  const entries = await database.query(queryOptions)
  if (entries.length === 0 && !allowEmpty) throw new Error('Query Failed!')
  return entries
}

However, this approach has its own issues. Can you spot which destructured properties are not being used to create the new object? It's also pretty gross that we're polluting this function with so many local variables.

As shown before, this kind of feature request seems to keep cropping up with different syntaxes or styles, so I hope one of these feature requests gains some traction. I just thought I would throw another syntax into the conversation that I find to be intuitive, and wanted to know what other people's thoughts were on the matter.

Thanks for taking another hit at this! Personally I'm a fan of Ron Buckton's property shorthands based on object literals - they have the advantage of easily merging multiple objects and allowing to take in other properties as well.

The problem I see with user.{city: someCity = 'n.a.'} is that it's hard to tell (at least, not immediately obvious) whether it means {city: user.someCity ?? 'n.a.'} or {someCity: user.city ?? 'n.a.'}. The syntax looks a bit like destructuring, but without a clear target.

What do you think about {...user.{city: someCity = 'n.a.'}}, i.e. combining this with spread syntax in an object literal?

I wasn't familiar with the Bosque programming language and I like the syntax it offers to that use-case.
I'll mention that the as in my proposal might conflict with typescript type assertion

I do like Ron Buckton's syntax too, so I would be very happy if that were to get in instead. And you're right, it does have the advantage of being able to merge properties from different objects. For comparison, that kind of behavior would look like the bellow snippet whith the object restructuring syntax:

const newObj = {
  ...obj1.{ a, b, c },
  ...obj2.{ d, e },
  f: 2,
}

which looks pretty good too :), and combines the object restructuring syntax with the spread syntax, like you mentioned.

Your concern with user.{ city: someCity } and knowing if we're renaming city to someCity or vice verca is a valid concern. I will point out though that when I first learned object destructuring, I struggled a bit with the same issue. In the snippet const { city: someCity } = user - it's not obvious weather we're extracting city or someCity from user (except for those who have become familiar with the syntax).