Index

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

const f = wrap(function f() {});

const C = wrap(class C {});

符合此规则的正确代码示例:

🌐 Examples of correct code for this rule:

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

const a = 3;
function b(c) {
    const d = 10;
    if (c) {
        const e = 20;
    }
}

/*
 * Function and class names are allowed to shadow a variable
 * if the function/class expression is assigned to the variable
 * as its initializer or default value.
 */
const f = function f() {};
const g = foo ? (bar || function g() {}) : baz;
const { h = function h() {} } = obj;
function qux(i = function i() {}) {}
const C = class C {};

选项

🌐 Options

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

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

  • "builtinGlobals"
  • "hoist"
  • "allow"
  • "ignoreOnInitialization"
  • "ignoreTypeValueShadow"(仅限 TypeScript)
  • "ignoreFunctionTypeParameterNameValueShadow"(仅限 TypeScript)
{
    "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(默认情况下)- 在定义外部函数之前报告阴影。
  • all - 在定义外部变量/函数之前报告所有阴影。
  • never - 在定义外部变量/函数之前,永远不要报告阴影。
  • types(仅限 TypeScript)- 在定义外部类型之前报告覆盖。
  • functions-and-types(仅限 TypeScript)- 在定义外部函数和类型之前报告覆盖。

提升:功能

🌐 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 位于外层作用域的 function 声明之前,但这是不正确的。

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

提升:全部

🌐 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() {}

提升:从不

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

起重机:类型

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

提升:功能和类型

🌐 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';
}

注意: 屏蔽(Shadowing)特指在不同的嵌套作用域中存在两个相同的标识符。这不同于重声明(redeclaration),重声明是指两个相同的标识符在同一个作用域中。重声明由 no-redeclare 规则涵盖。

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?

这不是一个错误——规则完全按预期工作!报告是正确的,因为枚举的一个不太为人知的方面:枚举成员会在枚举自身的作用域内引入一个变量,使它们可以在不需要限定符的情况下被引用。

🌐 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 中引入。

进阶读物

资源