简体   繁体   中英

Omit property variable when using object destructuring

Here is an example:

const initObject = {
  a: 0,
  b: 0,
  c: 0
}

const { a, ...rest } = initObject

We're omitting property a from the object, but then const a is assigned a value, but never used - error from eslint (no-unused-vars). Is it possible to completely omit const a ?

A possible way is to use // eslint-disable-next-line no-unused-vars

eg

// eslint-disable-next-line no-unused-vars
const { a, ...rest } = initObject

Or by usingignoreRestSiblings

The ignoreRestSiblings option is a boolean (default: false). Using a Rest Property it is possible to “omit” properties from an object, but by default the sibling properties are marked as “unused”. With this option enabled the rest property's siblings are ignored.

eg

/*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/
// 'a' is ignored because it has a rest property sibling.
const { a, ...rest } = initObject;

More info about no-unused-vars


But if your goal is to remove the property a , there is another way.
You can use delete operator.

From MDN documentation

The JavaScript delete operator removes a property from an object

eg

 const initObject = { a: 0, b: 0, c: 0 } const rest = { ...initObject }; // create a shallow copy delete rest.a; console.log(rest);

error from eslint (no-unused-vars).

The no-unused-vars rules has two configuration options that will help with your use case:

  • TheignoreRestSiblings option is a boolean that defaults to false . When enabled, the rest property's siblings are ignored. This is exactly what you need!
  • The varsIgnorePattern option specifies a regexp pattern for variable names not to be checked for usage. This allows us to make an exception for the common underscore identifier to explicitly mark unused variables with { "varsIgnorePattern": "^_" } .

     const { a:_, ...rest } = initObject; // ^^

    Unfortunately you still need to avoid multiple declarations of the _ variable, so to omit multiple properties you'd need to do something like { a:_a, b:_b, ...rest } = … .

Is it possible to completely omit const a ?

A bad hack that completely avoids introducing any identifier would be to use

const { a:{}, ...rest } = initObject;
//       ^^^

that further destructures the .a property value into an object, but for this you need to ensure that the property exists and doesn't hold a null or undefined value.

This may seem a trivial deviation from @R3tep's answer , but it avoids the pitfall of marking all the variables in the declaration as used:

const initObject = {
  a: 0,
  b: 0,
  c: 0
}

const {
  a, // eslint-disable-line no-unused-vars
  ...rest
} = initObject

Now if rest is unused, it will still cause an eslint error.


In regards to the question

what's the correct way to remove a property then?

I'm going to answer the question you should have asked instead. The correct way to handle an object with properties you don't need is to rewrite your logic in a way that implicitly ignores them.

  1. If you just need the properties b and c , destructure only those properties:

     const { b, c } = initObject

    Don't even acknowledge that a exists if you don't need it.

  2. If your input has a lot of specific properties you need to deal with, and you can't assume that initObject already has the exact layout it needs, then avoid the temptation to use reflection methods or syntax like Object.entries() , for...in , object spread and rest syntax , etc.

    Continue to handle the specific properties you need on an individual basis, and break up your logic into separable functions that each deal with a manageable subset of the properties, if necessary.

    On the other hand, if you can precondition your input to have the exact layout you already need (eg you are able to assume that initObject only has b and c ), then feel free to use reflection -- that's exactly what it's for.

  3. If neither of the above two points applies to you and you still find that you really want initObject to have a lot of arbitrary properties, and a few that you want to ignore, then you should use the suggestion at the beginning of this answer (or one of the other answers that works for you).

    However, as noted, this is code-smell and an indicator that your logic needs to be more lenient with the layout of the object 1 , or your preconditions for the input need to change 2 .

You can create a IIFE and pass object to it.

 const initObject = { a: 0, b: 0, c: 0 } const res = (({a,...rest}) => (a,rest))(initObject); console.log(res)

An option that technically fulfills the linter rules would be to declare rest upfront, destructure the a property into rest , and then use rest syntax to put the rest of the object into the rest variable name:

 const initObject = { a: 0, b: 0, c: 0 }; let rest; ({ a: rest, ...rest } = initObject); console.log(rest);

Unfortunately, if you want to avoid var , you can't do it in just a single line like

let { a: rest, ...rest } = initObject

because when the left-hand side of the { declares a variable, each new variable name on the right side is initialized separately - that is, to the interpreter it looks a bit like

let rest = initObject.a;
let rest = <everything else in initObject>

But duplicate let identifiers for the same variable name is not permitted. You could do it in one line with var , for which duplicate identifiers are permitted:

 const initObject = { a: 0, b: 0, c: 0 }; var { a: rest, ...rest } = initObject; console.log(rest);

But this is all a little bit odd. I'd prefer to configure/ignore the linter, or use something other than destructuring, as other answers have suggested.

You can create a shallow copy of the object using Object.assign() and simply delete the property.

 const initObject = { a: 0, b: 0, c: 0 } let rest = Object.assign({}, initObject); delete rest.a; console.log(rest); console.log(initObject);

try

const rest = ((o)=> (delete o.a,o))({...initObject});

 'use strict' const initObject = { a: 0, b: 0, c: 0 } const rest = ((o)=> (delete oa,o))({...initObject}); console.log({initObject,rest});

In order to omit (sanitize) the ID and the password properties, this is what finally worked for me in a single object.

This was the response from a Relationship between Products and Users Models.

   return {...product,
        author: [
            `username: ` + product['author'].username,
            `email: ` + product['author'].email,
        ]};

Otherwise for an array, I used:

    return products.map(product => [{ 
        'title' : product['title'],
        'description' : product['description'],
        'price' : product['price'],
        'updated' : product['updatedAt'],
        'author': {
            'username' : product['author'].username,
            'email' : product['author'].email,
        },                   
    }]);

I'm using PostgreSQL, Nest, Objection and Knex.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM