no-shadow
Запрещает объявлениям переменных затенять переменные, объявленные во внешней области видимости
Затенение — это процесс, при котором локальная переменная имеет то же имя, что и переменная во внешней области видимости. Например:
const a = 3;
function b() {
const a = 10;
}
В этом случае переменная a внутри b() затеняет переменную a в глобальной области видимости. Это может затруднить чтение кода, а обратиться к глобальной переменной становится невозможно.
Подробности правила
Это правило предназначено для устранения затенённых объявлений переменных.
Примеры неправильного кода для этого правила:
/*eslint no-shadow: "error"*/
const a = 3;
function b() {
const = 10;
}
const c = function () {
const = 10;
}
function d() {
a = 10;
}
d(a);
if (true) {
const = 5;
}
const f = wrap(function () {});
const C = wrap(class {});
Примеры правильного кода для этого правила:
/*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 {};
Параметры
Это правило принимает один параметр — объект со следующими свойствами:
"builtinGlobals""hoist""allow""ignoreOnInitialization"-
"ignoreTypeValueShadow"(только TypeScript) -
"ignoreFunctionTypeParameterNameValueShadow"(только TypeScript)
{
"no-shadow": ["error", { "builtinGlobals": false, "hoist": "functions", "allow": [], "ignoreOnInitialization": false }]
}
builtinGlobals
По умолчанию параметр builtinGlobals имеет значение false. Если задано значение true, правило запрещает затенять встроенные глобальные переменные: Object, Array, Number и так далее.
Примеры неправильного кода для параметра { "builtinGlobals": true }:
/*eslint no-shadow: ["error", { "builtinGlobals": true }]*/
function foo() {
const = 0;
}
hoist
Параметр hoist имеет пять вариантов:
-
functions(по умолчанию) — сообщает о затенении до объявления внешних функций. -
all— сообщает обо всех случаях затенения до объявления внешних переменных и функций. -
never— никогда не сообщает о затенении до объявления внешних переменных и функций. -
types(только TypeScript) — сообщает о затенении до объявления внешних типов. -
functions-and-types(только TypeScript) — сообщает о затенении до объявления внешних функций и типов.
hoist: functions
Примеры неправильного кода для параметра { "hoist": "functions" } (значение по умолчанию):
/*eslint no-shadow: ["error", { "hoist": "functions" }]*/
if (true) {
const = 6;
}
function b() {}
Несмотря на то что const b в инструкции if расположено перед объявлением функции во внешней области видимости, этот код неправильный.
Примеры правильного кода для параметра { "hoist": "functions" } (значение по умолчанию):
/*eslint no-shadow: ["error", { "hoist": "functions" }]*/
if (true) {
const a = 3;
}
const a = 5;
Поскольку const a в инструкции if расположено перед объявлением переменной во внешней области видимости, этот код правильный.
hoist: all
Примеры неправильного кода для параметра { "hoist": "all" }:
/*eslint no-shadow: ["error", { "hoist": "all" }]*/
if (true) {
const = 3;
const = 6;
}
const a = 5;
function b() {}
hoist: never
Примеры правильного кода для параметра { "hoist": "never" }:
/*eslint no-shadow: ["error", { "hoist": "never" }]*/
if (true) {
const a = 3;
const b = 6;
}
const a = 5;
function b() {}
Поскольку const a и const b в инструкции if расположены перед объявлениями во внешней области видимости, этот код правильный.
hoist: types
Примеры неправильного кода для параметра { "hoist": "types" }:
/*eslint no-shadow: ["error", { "hoist": "types" }]*/
type Bar<> = 1;
type Foo = 1;
hoist: functions-and-types
Примеры неправильного кода для параметра { "hoist": "functions-and-types" }:
/*eslint no-shadow: ["error", { "hoist": "functions-and-types" }]*/
// types
type Bar<> = 1;
type Foo = 1;
// functions
if (true) {
const = 6;
}
function b() {}
allow
Параметр allow — это массив имён идентификаторов, затенение которых разрешено. Например: "resolve", "reject", "done", "cb".
Примеры правильного кода для параметра { "allow": ["done"] }:
/*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, правило не сообщает о затенении переменных в их инициализаторах, когда предполагается, что затенённая переменная ещё не инициализирована.
Затеняемая переменная должна находиться слева. Затеняющая переменная должна находиться справа и быть объявлена в функции обратного вызова или в IIFE.
Примеры неправильного кода для параметра { "ignoreOnInitialization": "true" }:
/*eslint no-shadow: ["error", { "ignoreOnInitialization": true }]*/
const x = => x;
Поскольку затеняющая переменная x затенит уже инициализированную затеняемую переменную x.
Примеры правильного кода для параметра { "ignoreOnInitialization": true }:
/*eslint no-shadow: ["error", { "ignoreOnInitialization": true }]*/
const x = foo(x => x)
const y = (y => y)()
Обоснование для функций обратного вызова заключается в предположении, что они будут вызваны во время инициализации, поэтому к моменту использования затеняющей переменной затеняемая переменная ещё не будет инициализирована.
ignoreTypeValueShadow
Следует ли игнорировать типы с тем же именем, что и у переменной. Значение по умолчанию: true.
Обычно это безопасно, поскольку нельзя использовать переменные в позициях типов без оператора typeof, поэтому риск путаницы невелик.
Примеры правильного кода с { "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.
ignoreFunctionTypeParameterNameValueShadow
Следует ли игнорировать параметры функций с тем же именем, что и у переменной. Значение по умолчанию: true.
Каждый аргумент типа функции создаёт переменную-значение в области видимости типа функции. Это сделано для того, чтобы позднее можно было ссылаться на тип с помощью оператора typeof:
type Func = (test: string) => typeof test;
declare const fn: Func;
const result = fn('str'); // typeof result === string
Это означает, что аргументы типа функции затеняют имена переменных-значений во внешних областях видимости:
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 в позиции типа возвращаемого значения типа функции, этот параметр можно безопасно включить.
Примеры правильного кода с { "ignoreFunctionTypeParameterNameValueShadow": true }:
/*eslint no-shadow: ["error", { "ignoreFunctionTypeParameterNameValueShadow": true }]*/
const test = 1;
type Func = (test: string) => typeof test;
Почему правило сообщает о членах перечисления с тем же именем, что и у переменной во внешней области видимости?
Это не ошибка — правило работает именно так, как задумано! Сообщение корректно из-за малоизвестной особенности перечислений: их члены вводят переменную в собственной области видимости перечисления, что позволяет обращаться к ним без квалификатора.
Рассмотрим простой пример:
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. Из-за такого поведения член перечисления затеняет внешнее объявление переменной.
Связанные правила
Версия
Это правило появилось в ESLint v0.0.9.
Дополнительная литература
Ресурсы
© OpenJS Foundation and other contributors
Licensed under the MIT License.
https://eslint.org/docs/latest/rules/no-shadow