Making Sense of 'No Shadowed Variable' tslint Warning
Asked Answered
F

7

51

I have a function that checks for the current stage in a sequential stream, based on a particular discipline that is passed in, and, according to that value, assigns the next value in my Angular 2 app. It looks something like this:

private getNextStageStep(currentDisciplineSelected) {
    const nextStageStep = '';
        if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 1') {
            const nextStageStep = 'step 2';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 2') {
            const nextStageStep = 'step 3';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 3') {
            const nextStageStep = 'step 4';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 4') {
            const nextStageStep = 'step 5';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 5') {
            const nextStageStep = 'step 6';
    }
    return nextStageStep;
}

What I'm doing here is returning the value of nextStageStep, because that's what I'll be then passing in order for the correct stage step to happen.

Right now, my tslint is underlining each of the nextStageStep variable occurrences with the warning no shadowed variables. If I remove the line where I initialize to an empty string that warning goes away, but then I get the error, Cannot find nextStageStep showing up in my return statement.

What is the issue with the original shadowed variable warning, and is there an alternative way to write this, and/or should I simply ignore the tslint warning in this situation?

Fransiscafransisco answered 30/6, 2017 at 18:47 Comment(0)
A
70

The linter complains because you are redefining the same variable multiple times. Thus replacing the ones in the closure containing it.

Instead of redeclaring it just use it:

private getNextStageStep(currentDisciplineSelected) {
    let nextStageStep = '';
        if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 1') {
             nextStageStep = 'step 2';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 2') {
             nextStageStep = 'step 3';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 3') {
             nextStageStep = 'step 4';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 4') {
             nextStageStep = 'step 5';
        } else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 5') {
             nextStageStep = 'step 6';
    }
    return nextStageStep;
}
Anaclitic answered 30/6, 2017 at 18:52 Comment(1)
well yeah because it's a constant. change it to a let or varAnaclitic
W
9

In general,
When a variable in a local scope and a variable in the containing scope have the same name, shadowing occurs. Shadowing makes it impossible to access the variable in the containing scope and obscures to what value an identifier actually refers to.

const a = 'no shadow';
function print() {
    console.log(a);
}
print(); // logs 'no shadow'.

const a = 'no shadow';
function print() {
    const a = 'shadow'; // TSLint will complain here.
    console.log(a);
}
print(); // logs 'shadow'.

Refer to this article for code samples explaining this.

Windermere answered 9/1, 2019 at 7:47 Comment(0)
T
5

This has to do with defining the same variable in different scopes. You are defining nextStageStep within the function scope & also within each if block. One option is to get rid of the variable declarations in the if blocks

if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 1') {
   nextStageStep = 'step 2';
} else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 2') {
   nextStageStep = 'step 3';
} else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 3') {
   nextStageStep = 'step 4';
} else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 4') {
   nextStageStep = 'step 5';
} else if (this.stageForDiscipline(this.currentDisciplineSelected) === 'step 5') {
   nextStageStep = 'step 6';
}

Here is a good resource on shadowed variables http://eslint.org/docs/rules/no-shadow

Teahan answered 30/6, 2017 at 18:52 Comment(1)
This won't work unless nextStageStep is declared as let, instead of constCarouse
F
4

You are re-declaring the same variable const nextStageStep in each if block.

Juste replace const nextStageStep = 'step 2'; with nextStageStep = 'step 2'; (and all the other if cases) and it'll be all right.

Forage answered 30/6, 2017 at 18:52 Comment(0)
F
4

Addording to : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const

ES6 const is BLOCK-SCOPED, thus:


{
    const TAG='<yourIt>';
    console.log(TAG);
 }

 {
  const TAG = '<touchingBase NoImNOt="true">';
  console.log(TAG);
 }

 console.log(TAG);  // ERROR expected

AFAICT, this is NOT a case of shadowing - each of the constants is soped correctly within its braces.

If we cannot re-use variable names, we will wind up with unreadable programs that obscure. rather than inform.

I believe the warning is wrong-headed

Forest answered 2/8, 2018 at 19:23 Comment(1)
Your example isn't shadowing the constant T, but the original question is with nextStageStep. Shadowing doesn't mean the constant is being redefined illegally (const x = 1; const x = 2), just that there could be an error if you mistake which var/const/let you refer to. It's a matter of opinion whether you allow shadowing, but you can say that this rule picked up the OP's mistake here, so you can see it had some purpose.Madlynmadman
G
1

First of all, even if you proceed with the warnings, your function "getNextStageStep()" will always return the empty value,

  • Because "const" a is block-scoped variable, and

  • It does not supports re-defining of value [Initialized value cannot be changed].

In return block variable "nextStageStep" contains empty string value, and inner blocks "nextStageStep" variables will not mask or override the outer block's "nextStageStep" variable value.

So whenever you return "nextStageStep", it will always return empty string.

Inner blocks "nextStageStep" variables scope is within that if block only and here outer block "nextStageStep" variable is completely different from inner block "nextStageStep" variables.

So if you want your code to work and if you must want to use const variables, then use multiple return statements within if blocks.

Below is the code I checked and working fine. you can use it according to your requirement.

function  getNextStageStep(currentDisciplineSelected) {
    const nextStageStep = '';
    if (currentDisciplineSelected === 'step 1') {
        const nextStageStep = 'step 2';
        return nextStageStep;
    } else if (currentDisciplineSelected === 'step 2') {
        const nextStageStep = 'step 3';
        return nextStageStep;
    } else if (currentDisciplineSelected === 'step 3') {
        const nextStageStep = 'step 4';
        return nextStageStep;
    } else if (currentDisciplineSelected === 'step 4') {
        const nextStageStep = 'step 5';
        return nextStageStep;
    } else if (currentDisciplineSelected === 'step 5') {
        const nextStageStep = 'step 6';
        return nextStageStep;
    }
    return nextStageStep;
}
console.log(getNextStageStep('step 1'));

But instead writing these many return statements better to use let variable which allows you to re-define the variable value. For your problem I think @toskv solution is suitable.

Grau answered 1/5, 2019 at 20:52 Comment(0)
B
-1

Locate and open your tslint.json file and set the following setting to false

 "no-shadowed-variable": false,

When using visual studio, a restart of visual studio might be required.

Bissextile answered 1/5, 2020 at 15:0 Comment(2)
It seems dangerous to recommend disabling a linting rule when the provided code example clearly contains an unwanted mistake the rule is made to prevent.Impious
Should rename the shadow variable instead — disabling it is dangerous!Axum

© 2022 - 2024 — McMap. All rights reserved.