Skip to content

Commit

Permalink
fix(eslint-plugin): [no-shadow] ignore ordering of type declarations (#…
Browse files Browse the repository at this point in the history
…10593)

* initial implementation

* add relevant tests

* add docs

* snapshots

* match original implementation
  • Loading branch information
ronami authored Jan 13, 2025
1 parent 6dda0a4 commit 63135f7
Show file tree
Hide file tree
Showing 5 changed files with 551 additions and 14 deletions.
34 changes: 34 additions & 0 deletions packages/eslint-plugin/docs/rules/no-shadow.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,52 @@ It adds support for TypeScript's `this` parameters and global augmentation, and
This rule adds the following options:

```ts
type AdditionalHoistOptionEntries = 'types' | 'functions-and-types';

type HoistOptionEntries =
| BaseNoShadowHoistOptionEntries
| AdditionalHoistOptionEntries;

interface Options extends BaseNoShadowOptions {
hoist?: HoistOptionEntries;
ignoreTypeValueShadow?: boolean;
ignoreFunctionTypeParameterNameValueShadow?: boolean;
}

const defaultOptions: Options = {
...baseNoShadowDefaultOptions,
hoist: 'functions-and-types',
ignoreTypeValueShadow: true,
ignoreFunctionTypeParameterNameValueShadow: true,
};
```

### hoist: `types`

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

```ts option='{ "hoist": "types" }' showPlaygroundButton
type Bar<Foo> = 1;
type Foo = 1;
```

### hoist: `functions-and-types`

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

```ts option='{ "hoist": "functions-and-types" }' showPlaygroundButton
// types
type Bar<Foo> = 1;
type Foo = 1;

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

function b() {}
```

### `ignoreTypeValueShadow`

{/* insert option description */}
Expand Down
46 changes: 34 additions & 12 deletions packages/eslint-plugin/src/rules/no-shadow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type Options = [
{
allow?: string[];
builtinGlobals?: boolean;
hoist?: 'all' | 'functions' | 'never';
hoist?: 'all' | 'functions' | 'functions-and-types' | 'never' | 'types';
ignoreFunctionTypeParameterNameValueShadow?: boolean;
ignoreOnInitialization?: boolean;
ignoreTypeValueShadow?: boolean;
Expand All @@ -28,6 +28,13 @@ const allowedFunctionVariableDefTypes = new Set([
AST_NODE_TYPES.TSConstructorType,
]);

const functionsHoistedNodes = new Set([AST_NODE_TYPES.FunctionDeclaration]);

const typesHoistedNodes = new Set([
AST_NODE_TYPES.TSInterfaceDeclaration,
AST_NODE_TYPES.TSTypeAliasDeclaration,
]);

export default createRule<Options, MessageIds>({
name: 'no-shadow',
meta: {
Expand Down Expand Up @@ -63,7 +70,7 @@ export default createRule<Options, MessageIds>({
type: 'string',
description:
'Whether to report shadowing before outer functions or variables are defined.',
enum: ['all', 'functions', 'never'],
enum: ['all', 'functions', 'functions-and-types', 'never', 'types'],
},
ignoreFunctionTypeParameterNameValueShadow: {
type: 'boolean',
Expand All @@ -88,7 +95,7 @@ export default createRule<Options, MessageIds>({
{
allow: [],
builtinGlobals: false,
hoist: 'functions',
hoist: 'functions-and-types',
ignoreFunctionTypeParameterNameValueShadow: true,
ignoreOnInitialization: false,
ignoreTypeValueShadow: true,
Expand Down Expand Up @@ -513,15 +520,30 @@ export default createRule<Options, MessageIds>({
const inner = getNameRange(variable);
const outer = getNameRange(scopeVar);

return !!(
inner &&
outer &&
inner[1] < outer[0] &&
// Excepts FunctionDeclaration if is {"hoist":"function"}.
(options.hoist !== 'functions' ||
!outerDef ||
outerDef.node.type !== AST_NODE_TYPES.FunctionDeclaration)
);
if (!inner || !outer || inner[1] >= outer[0]) {
return false;
}

if (!outerDef) {
return true;
}

if (options.hoist === 'functions') {
return !functionsHoistedNodes.has(outerDef.node.type);
}

if (options.hoist === 'types') {
return !typesHoistedNodes.has(outerDef.node.type);
}

if (options.hoist === 'functions-and-types') {
return (
!functionsHoistedNodes.has(outerDef.node.type) &&
!typesHoistedNodes.has(outerDef.node.type)
);
}

return true;
}

/**
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 63135f7

Please # to comment.