Spec-Zone.ru › ESLint

no-shadow

Запрещает объявлениям переменных затенять переменные, объявленные во внешней области видимости

Затенение — это процесс, при котором локальная переменная имеет то же имя, что и переменная во внешней области видимости. Например:

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

В этом случае переменная a внутри b() затеняет переменную a в глобальной области видимости. Это может затруднить чтение кода, а обратиться к глобальной переменной становится невозможно.

Подробности правила

Это правило предназначено для устранения затенённых объявлений переменных.

Примеры неправильного кода для этого правила:

Открыть в Playground
/*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 {});

Примеры правильного кода для этого правила:

Открыть в Playground
/*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 }:

Открыть в Playground
/*eslint no-shadow: ["error", { "builtinGlobals": true }]*/

function foo() {
    const Object = 0;
}

hoist

Параметр hoist имеет пять вариантов:

  • functions (по умолчанию) — сообщает о затенении до объявления внешних функций.
  • all — сообщает обо всех случаях затенения до объявления внешних переменных и функций.
  • never — никогда не сообщает о затенении до объявления внешних переменных и функций.
  • types (только TypeScript) — сообщает о затенении до объявления внешних типов.
  • functions-and-types (только TypeScript) — сообщает о затенении до объявления внешних функций и типов.

hoist: functions

Примеры неправильного кода для параметра { "hoist": "functions" } (значение по умолчанию):

Открыть в Playground
/*eslint no-shadow: ["error", { "hoist": "functions" }]*/

if (true) {
    const b = 6;
}

function b() {}

Несмотря на то что const b в инструкции if расположено перед объявлением функции во внешней области видимости, этот код неправильный.

Примеры правильного кода для параметра { "hoist": "functions" } (значение по умолчанию):

Открыть в Playground
/*eslint no-shadow: ["error", { "hoist": "functions" }]*/

if (true) {
    const a = 3;
}

const a = 5;

Поскольку const a в инструкции if расположено перед объявлением переменной во внешней области видимости, этот код правильный.

hoist: all

Примеры неправильного кода для параметра { "hoist": "all" }:

Открыть в Playground
/*eslint no-shadow: ["error", { "hoist": "all" }]*/

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

const a = 5;
function b() {}

hoist: never

Примеры правильного кода для параметра { "hoist": "never" }:

Открыть в Playground
/*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" }:

Открыть в Playground
/*eslint no-shadow: ["error", { "hoist": "types" }]*/

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

hoist: functions-and-types

Примеры неправильного кода для параметра { "hoist": "functions-and-types" }:

Открыть в Playground
/*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".

Примеры правильного кода для параметра { "allow": ["done"] }:

Открыть в Playground
/*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" }:

Открыть в Playground
/*eslint no-shadow: ["error", { "ignoreOnInitialization": true }]*/

const x = x => x;

Поскольку затеняющая переменная x затенит уже инициализированную затеняемую переменную x.

Примеры правильного кода для параметра { "ignoreOnInitialization": true }:

Открыть в Playground
/*eslint no-shadow: ["error", { "ignoreOnInitialization": true }]*/

const x = foo(x => x)

const y = (y => y)()

Обоснование для функций обратного вызова заключается в предположении, что они будут вызваны во время инициализации, поэтому к моменту использования затеняющей переменной затеняемая переменная ещё не будет инициализирована.

ignoreTypeValueShadow

Следует ли игнорировать типы с тем же именем, что и у переменной. Значение по умолчанию: true.

Обычно это безопасно, поскольку нельзя использовать переменные в позициях типов без оператора typeof, поэтому риск путаницы невелик.

Примеры правильного кода с { "ignoreTypeValueShadow": true }:

Открыть в Playground
/*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 }:

Открыть в Playground
/*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. Из-за такого поведения член перечисления затеняет внешнее объявление переменной.

Связанные правила

  • no-shadow-restricted-names

Версия

Это правило появилось в ESLint v0.0.9.

Дополнительная литература

Затенение переменных — Википедия
en.wikipedia.org

Ресурсы

  • Исходный код правила
  • Исходный код тестов

© OpenJS Foundation and other contributors
Licensed under the MIT License.
https://eslint.org/docs/latest/rules/no-shadow

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API