By Alexander Kim


2019-05-15 14:23:33 8 Comments

Here's example:

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

const { a, ...rest } = initObject

We're omitting a property 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?

7 comments

@R3tep 2019-05-15 14:34:18

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);

@Alexander Kim 2019-05-15 14:34:49

Thanks, but i don't want to disable the warning, i want to do it "the right way" :)

@R3tep 2019-05-15 14:35:10

@AlexanderKim It's the right way for me

@Alexander Kim 2019-05-15 14:44:21

Well, guess i'll just ignore it. But i wonder, how unused vars are affecting an app performance?

@R3tep 2019-05-15 14:52:08

@AlexanderKim Interesting question. But unfortunately I haven't answers about performance of unused variable.

@Alexander Kim 2019-05-15 15:13:28

Seems like delete is a better option, indeed.

@Alexander Kim 2019-05-15 15:22:04

Why you have changed const rest = initObject to const rest = { ...initObject } ?

@Patrick Roberts 2019-05-15 15:24:26

@AlexanderKim because they're different. If you reference initObject instead of creating a shallow copy, delete rest.a will also modify initObject.

@CertainPerformance 2019-05-15 22:07:01

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.

@Bergi 2019-05-15 21:24:37

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.

@Patrick Roberts 2019-05-16 12:35:11

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 2019-05-16 12:44:22

By the way, for your last block, const { a: {} = {}, ...rest } = initObject would allow a to be undefined but looks even worse.

@Patrick Roberts 2019-05-15 14:45:23

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.

@Alexander Kim 2019-05-15 14:46:30

Thanks, what about performance, if i'm gonna have loads of places with unused vars?

@Patrick Roberts 2019-05-15 14:52:52

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 2019-05-15 14:54:40

@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.

@Alexander Kim 2019-05-15 14:59:14

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

@R3tep 2019-05-15 15:07:54

@AlexanderKim You can use the operator delete

@Patrick Roberts 2019-05-15 15:16:56

@AlexanderKim see my updated answer.

@Delioth 2019-05-15 21:32:53

@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.

@Kaiido 2019-05-16 00:03:18

@Delioth 1ms is a lot. Here we are rather talking about 1/100μs or less. And your point stands better.

@Kamil Kiełczewski 2019-05-15 14:31:09

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});

@Patrick Roberts 2019-05-15 14:34:46

Without the comma operator, I'm pretty sure eslint will still complain that a is unused.

@amrender singh 2019-05-15 14:28:16

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);

@Maheer Ali 2019-05-15 14:25:40

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)

@Pointy 2019-05-15 14:26:39

It's possible that eslint would still complain about this. (My solution would be to ignore eslint, but that's just me.)

@Maheer Ali 2019-05-15 14:28:17

@Pointy Maybe using comma operator will work.See the update.

Related Questions

Sponsored Content

71 Answered Questions

[SOLVED] What is the most efficient way to deep clone an object in JavaScript?

62 Answered Questions

[SOLVED] How do I correctly clone a JavaScript object?

41 Answered Questions

[SOLVED] Sort array of objects by string property value

38 Answered Questions

[SOLVED] How do I remove a property from a JavaScript object?

44 Answered Questions

[SOLVED] How to check if an object is an array?

60 Answered Questions

[SOLVED] How can I merge properties of two JavaScript objects dynamically?

46 Answered Questions

25 Answered Questions

[SOLVED] Iterate through object properties

41 Answered Questions

[SOLVED] Detecting an undefined object property

18 Answered Questions

Sponsored Content