46

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?

YesThatIsMyName
  • 1,585
  • 3
  • 23
  • 30
Alexander Kim
  • 17,304
  • 23
  • 100
  • 157

8 Answers8

44

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

e.g.

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

Or by using ignoreRestSiblings

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.

e.g.

/*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

e.g.

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

const rest = { ...initObject }; // create a shallow copy
delete rest.a;

console.log(rest);
R3tep
  • 12,512
  • 10
  • 48
  • 75
23

error from eslint (no-unused-vars).

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

  • The ignoreRestSiblings 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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I didn't know about these two rules but this was literally the first thing I suggested in comments (it's deleted now). Have my upvote – Patrick Roberts May 16 '19 at 12:35
  • 1
    By the way, for your last block, `const { a: {} = {}, ...rest } = initObject` would allow `a` to be `undefined` but looks even worse. – Patrick Roberts May 16 '19 at 12:44
9

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 (e.g. 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 object1, or your preconditions for the input need to change2.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Thanks, what about performance, if i'm gonna have loads of places with unused vars? – Alexander Kim May 15 '19 at 14:46
  • 1
    But, if after evaluating what I just pointed out, this is still deemed necessary, then performance-wise it's about the equivalent of declaring an unused pointer, which is to say that it's negligible. Anyway it's possible the optimizer will notice it's unused and remove the declaration entirely. – Patrick Roberts May 15 '19 at 14:52
  • @AlexanderKim the performance cost is actually in the `...rest` part, which iterates over the remaining properties of `initObject` and copies them to `rest`, and is part of the reason it's considered code-smell. – Patrick Roberts May 15 '19 at 14:54
  • @AlexanderKim You can use the operator [`delete`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete) – R3tep May 15 '19 at 15:07
  • 5
    @AlexanderKim Performance costs are worthless to think about until they've 1) become an issue, and 2) you've profiled to be sure of exactly where your performance bottleneck is. Optimizing out 1 millisecond and 4 bytes isn't going to help you in 100% of usecases that you're using JavaScript with. – Delioth May 15 '19 at 21:32
  • 2
    @Delioth 1ms is a lot. Here we are rather talking about 1/100μs or less. And your point stands better. – Kaiido May 16 '19 at 00:03
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)
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
2

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.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

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);
amrender singh
  • 7,949
  • 3
  • 22
  • 28
1

try

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

'use strict'

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

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

console.log({initObject,rest});
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
-1

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.

Domiserver
  • 441
  • 6
  • 11