no-shadow

禁止在外部作用域中声明的隐藏变量的变量声明

影子化是局部变量与其包含范围内的变量共享相同名称的过程。例如:

¥Shadowing is the process by which a local variable shares the same name as a variable in its containing scope. For example:

const a = 3;
function b() {
    const a = 10;
}

在这种情况下,b() 内部的变量 a 将在全局作用域内隐藏变量 a。这可能会在阅读代码时造成混乱,并且无法访问全局变量。

¥In this case, the variable a inside of b() is shadowing the variable a in the global scope. This can cause confusion while reading the code and it’s impossible to access the global variable.

规则详情

¥Rule Details

该规则旨在消除阴影变量声明。

¥This rule aims to eliminate shadowed variable declarations.

此规则的错误代码示例:

¥Examples of incorrect code for this rule:

在线运行
/*eslint no-shadow: "error"*/

const a = 3;
function b() {
    const a = 10;
}

const c = function () {
    const a = 10;
}

function d(a) {
    a = 10;
}
d(a);

if (true) {
    const a = 5;
}

选项

¥Options

此规则采用一个选项,即对象,具有以下属性:

¥This rule takes one option, an object, with the following properties:

  • "builtinGlobals"

  • "hoist"

  • "allow"

  • "ignoreOnInitialization"

  • "ignoreTypeValueShadow"(仅限 TypeScript)

    ¥"ignoreTypeValueShadow" (TypeScript only)

  • "ignoreFunctionTypeParameterNameValueShadow"(仅限 TypeScript)

    ¥"ignoreFunctionTypeParameterNameValueShadow" (TypeScript only)

{
    "no-shadow": ["error", { "builtinGlobals": false, "hoist": "functions", "allow": [], "ignoreOnInitialization": false }]
}

builtinGlobals

builtinGlobals 选项默认为 false。如果它是 true,则该规则会阻止隐藏内置全局变量:ObjectArrayNumber 等等。

¥The builtinGlobals option is false by default. If it is true, the rule prevents shadowing of built-in global variables: Object, Array, Number, and so on.

{ "builtinGlobals": true } 选项的错误代码示例:

¥Examples of incorrect code for the { "builtinGlobals": true } option:

在线运行
/*eslint no-shadow: ["error", { "builtinGlobals": true }]*/

function foo() {
    const Object = 0;
}

hoist

hoist 选项有五种设置:

¥The hoist option has five settings:

  • functions(默认情况下) - 在定义外部函数之前报告阴影。

    ¥functions (by default) - reports shadowing before the outer functions are defined.

  • all - 在定义外部变量/函数之前报告所有阴影。

    ¥all - reports all shadowing before the outer variables/functions are defined.

  • never - 在定义外部变量/函数之前,永远不要报告阴影。

    ¥never - never report shadowing before the outer variables/functions are defined.

  • types(仅限 TypeScript) - 在定义外部类型之前报告覆盖。

    ¥types (TypeScript only) - reports shadowing before the outer types are defined.

  • functions-and-types(仅限 TypeScript) - 在定义外部函数和类型之前报告覆盖。

    ¥functions-and-types (TypeScript only) - reports shadowing before the outer functions and types are defined.

提升:functions

¥hoist: functions

默认 { "hoist": "functions" } 选项的错误代码示例:

¥Examples of incorrect code for the default { "hoist": "functions" } option:

在线运行
/*eslint no-shadow: ["error", { "hoist": "functions" }]*/

if (true) {
    const b = 6;
}

function b() {}

虽然 if 语句中的 const b 在外部作用域的函数声明之前,但它是不正确的。

¥Although const b in the if statement is before the function declaration in the outer scope, it is incorrect.

默认 { "hoist": "functions" } 选项的正确代码示例:

¥Examples of correct code for the default { "hoist": "functions" } option:

在线运行
/*eslint no-shadow: ["error", { "hoist": "functions" }]*/

if (true) {
    const a = 3;
}

const a = 5;

因为 if 语句中的 const a 在外部作用域的变量声明之前,所以是正确的。

¥Because const a in the if statement is before the variable declaration in the outer scope, it is correct.

提升:all

¥hoist: all

{ "hoist": "all" } 选项的错误代码示例:

¥Examples of incorrect code for the { "hoist": "all" } option:

在线运行
/*eslint no-shadow: ["error", { "hoist": "all" }]*/

if (true) {
    const a = 3;
    const b = 6;
}

const a = 5;
function b() {}

提升:never

¥hoist: never

{ "hoist": "never" } 选项的正确代码示例:

¥Examples of correct code for the { "hoist": "never" } option:

在线运行
/*eslint no-shadow: ["error", { "hoist": "never" }]*/

if (true) {
    const a = 3;
    const b = 6;
}

const a = 5;
function b() {}

因为 if 语句中的 const aconst b 在外部作用域的声明之前,所以它们是正确的。

¥Because const a and const b in the if statement are before the declarations in the outer scope, they are correct.

提升:types

¥hoist: types

{ "hoist": "types" } 选项的错误代码示例:

¥Examples of incorrect code for the { "hoist": "types" } option:

/*eslint no-shadow: ["error", { "hoist": "types" }]*/

type Bar<Foo> = 1;
type Foo = 1;

提升:functions-and-types

¥hoist: functions-and-types

{ "hoist": "functions-and-types" } 选项的错误代码示例:

¥Examples of incorrect code for the { "hoist": "functions-and-types" } option:

/*eslint no-shadow: ["error", { "hoist": "functions-and-types" }]*/

// types
type Bar<Foo> = 1;
type Foo = 1;

// functions
if (true) {
  const b = 6;
}

function b() {}

allow

allow 选项是一个标识符名称数组,允许对其进行遮蔽。例如,"resolve""reject""done""cb"

¥The allow option is an array of identifier names for which shadowing is allowed. For example, "resolve", "reject", "done", "cb".

{ "allow": ["done"] } 选项的正确代码示例:

¥Examples of correct code for the { "allow": ["done"] } option:

在线运行
/*eslint no-shadow: ["error", { "allow": ["done"] }]*/

import async from 'async';

function foo(done) {
  async.map([1, 2], function (e, done) {
    done(null, e * 2)
  }, done);
}

foo(function (err, result) {
  console.log({ err, result });
});

ignoreOnInitialization

ignoreOnInitialization 选项默认为 false。如果它是 true,那么当阴影变量可能仍未初始化时,它会阻止在其初始化程序中报告变量的阴影。

¥The ignoreOnInitialization option is false by default. If it is true, it prevents reporting shadowing of variables in their initializers when the shadowed variable is presumably still uninitialized.

阴影变量必须在左侧。阴影变量必须位于右侧并在回调函数或 IIFE 中声明。

¥The shadowed variable must be on the left side. The shadowing variable must be on the right side and declared in a callback function or in an IIFE.

{ "ignoreOnInitialization": "true" } 选项的错误代码示例:

¥Examples of incorrect code for the { "ignoreOnInitialization": "true" } option:

在线运行
/*eslint no-shadow: ["error", { "ignoreOnInitialization": true }]*/

const x = x => x;

因为阴影变量 x 将阴影已经初始化的阴影变量 x

¥Because the shadowing variable x will shadow the already initialized shadowed variable x.

{ "ignoreOnInitialization": true } 选项的正确代码示例:

¥Examples of correct code for the { "ignoreOnInitialization": true } option:

在线运行
/*eslint no-shadow: ["error", { "ignoreOnInitialization": true }]*/

const x = foo(x => x)

const y = (y => y)()

回调函数的基本原理是假设它们将在初始化期间被调用,因此在使用阴影变量时,阴影变量尚未初始化。

¥The rationale for callback functions is the assumption that they will be called during the initialization, so that at the time when the shadowing variable will be used, the shadowed variable has not yet been initialized.

ignoreTypeValueShadow

是否忽略与变量同名的类型。默认:true

¥Whether to ignore types named the same as a variable. Default: true.

这通常是安全的,因为如果没有 typeof 运算符,你就无法在类型位置使用变量,因此混淆的风险很小。

¥This is generally safe because you cannot use variables in type locations without a typeof operator, so there’s little risk of confusion.

此规则使用 { "ignoreTypeValueShadow": true } 的正确代码示例:

¥Examples of correct code with { "ignoreTypeValueShadow": true }:

/*eslint no-shadow: ["error", { "ignoreTypeValueShadow": true }]*/

type Foo = number;
interface Bar {
  prop: number;
}

function f() {
  const Foo = 1;
  const Bar = 'test';
}

注意:阴影特指位于不同嵌套作用域中的两个相同标识符。这与重新声明不同,重新声明是指两个相同的标识符位于同一作用域中。重新声明则由 no-redeclare 规则覆盖。

¥Note: Shadowing specifically refers to two identical identifiers that are in different, nested scopes. This is different from redeclaration, which is when two identical identifiers are in the same scope. Redeclaration is covered by the no-redeclare rule instead.

ignoreFunctionTypeParameterNameValueShadow

是否忽略与变量同名的函数参数。默认:true

¥Whether to ignore function parameters named the same as a variable. Default: true.

每个函数类型的参数都会在该函数类型的范围内创建一个值变量。这样做是为了方便你稍后使用 typeof 运算符引用该类型:

¥Each of a function type’s arguments creates a value variable within the scope of the function type. This is done so that you can reference the type later using the typeof operator:

type Func = (test: string) => typeof test;

declare const fn: Func;
const result = fn('str'); // typeof result === string

这意味着函数类型参数会在父作用域中遮蔽值变量名称:

¥This means that function type arguments shadow value variable names in parent scopes:

let test = 1;
type TestType = typeof test; // === number
type Func = (test: string) => typeof test; // this "test" references the argument, not the variable

declare const fn: Func;
const result = fn('str'); // typeof result === string

如果你在函数类型返回类型位置不使用 typeof 运算符,则可以安全地启用此选项。

¥If you do not use the typeof operator in a function type return type position, you can safely turn this option on.

此规则使用 { "ignoreFunctionTypeParameterNameValueShadow": true } 的正确代码示例:

¥Examples of correct code with { "ignoreFunctionTypeParameterNameValueShadow": true }:

/*eslint no-shadow: ["error", { "ignoreFunctionTypeParameterNameValueShadow": true }]*/

const test = 1;
type Func = (test: string) => typeof test;

为什么规则会报告与父作用域中的变量同名的枚举成员?

¥Why does the rule report on enum members that share the same name as a variable in a parent scope?

这不是 bug - 该规则完全按照预期工作!该报告是正确的,因为枚举中一个鲜为人知的方面:枚举成员在枚举自身范围内引入一个变量,允许在无需限定符的情况下引用它们。

¥This isn’t a bug — the rule is working exactly as intended! The report is correct because of a lesser-known aspect of enums: enum members introduce a variable within the enum’s own scope, allowing them to be referenced without needing a qualifier.

这里有一个简单的例子来解释:

¥Here’s a simple example to explain:

const A = 2;
enum Test {
  A = 1,
  B = A,
}

console.log(Test.B); // what should be logged?

乍一看,你可能会认为它应该记录 2,因为外部变量 A 的值是 2。但是,它实际上记录的是 1,即 Test.A 的值。发生这种情况的原因是,在枚举内部,B = A 被视为 B = Test.A。由于这种行为,枚举成员遮蔽了外部变量声明。

¥At first glance, you might think it should log 2, because the outer variable A’s value is 2. However, it actually logs 1, the value of Test.A. This happens because inside the enum B = A is treated as B = Test.A. Due to this behavior, the enum member has shadowed the outer variable declaration.

版本

此规则是在 ESLint v0.0.9 中引入。

进阶读物

资源