From 7edd79a921e7bf6297c784afe6e9d6c5f62a8152 Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Sun, 23 Mar 2025 15:27:34 +0530 Subject: [PATCH 1/9] feat: support TypeScript syntax in no-use-before-define --- lib/rules/no-use-before-define.js | 14 + tests/lib/rules/no-use-before-define.js | 1529 +++++++++++++++++++++++ 2 files changed, 1543 insertions(+) diff --git a/lib/rules/no-use-before-define.js b/lib/rules/no-use-before-define.js index 9c5669e8545f..5305f6dd5dee 100644 --- a/lib/rules/no-use-before-define.js +++ b/lib/rules/no-use-before-define.js @@ -237,6 +237,9 @@ module.exports = { classes: { type: "boolean" }, variables: { type: "boolean" }, allowNamedExports: { type: "boolean" }, + enums: { type: "boolean" }, + typedefs: { type: "boolean" }, + ignoreTypeReferences: { type: "boolean" }, }, additionalProperties: false, }, @@ -250,6 +253,9 @@ module.exports = { functions: true, variables: true, allowNamedExports: false, + enums: true, + typedefs: true, + ignoreTypeReferences: true, }, ], @@ -310,6 +316,14 @@ module.exports = { return false; } + if (!options.enums && definitionType === "TSEnumName") { + return false; + } + + if (!options.typedefs && definitionType === "Type") { + return false; + } + return true; } diff --git a/tests/lib/rules/no-use-before-define.js b/tests/lib/rules/no-use-before-define.js index 87c83b6ac4d9..da4ccd37a288 100644 --- a/tests/lib/rules/no-use-before-define.js +++ b/tests/lib/rules/no-use-before-define.js @@ -1676,3 +1676,1532 @@ ruleTester.run("no-use-before-define", rule, { }, ], }); + +const ruleTesterTypeScript = new RuleTester({ + languageOptions: { + parser: require("@typescript-eslint/parser"), + }, +}); + +const parserOptions = { ecmaVersion: 6 }; + +ruleTesterTypeScript.run("no-use-before-define", rule, { + valid: [ + ` + type foo = 1; + const x: foo = 1; + `, + ` + type foo = 1; + type bar = foo; + `, + ` + interface Foo {} + const x: Foo = {}; + `, + ` + var a = 10; + alert(a); + `, + ` + function b(a) { + alert(a); + } + `, + 'Object.hasOwnProperty.call(a);', + ` + function a() { + alert(arguments); + } + `, + 'declare function a();', + ` + declare class a { + foo(); + } + `, + 'const updatedAt = data?.updatedAt;', + ` + function f() { + return function t() {}; + } + f()?.(); + `, + ` + var a = { b: 5 }; + alert(a?.b); + `, + { + code: ` + a(); + function a() { + alert(arguments); + } + `, + options: ['nofunc'], + }, + { + code: ` + (() => { + var a = 42; + alert(a); + })(); + `, + languageOptions: { parserOptions }, + }, + ` + a(); + try { + throw new Error(); + } catch (a) {} + `, + { + code: ` + class A {} + new A(); + `, + languageOptions: { parserOptions }, + }, + ` + var a = 0, + b = a; + `, + { code: 'var { a = 0, b = a } = {};', languageOptions: { parserOptions } }, + { code: 'var [a = 0, b = a] = {};', languageOptions: { parserOptions } }, + ` + function foo() { + foo(); + } + `, + ` + var foo = function () { + foo(); + }; + `, + ` + var a; + for (a in a) { + } + `, + { + code: ` + var a; + for (a of a) { + } + `, + languageOptions: { parserOptions }, + }, + + // Block-level bindings + { + code: ` + 'use strict'; + a(); + { + function a() {} + } + `, + languageOptions: { parserOptions }, + }, + { + code: ` + 'use strict'; + { + a(); + function a() {} + } + `, + languageOptions: { parserOptions }, + options: ['nofunc'], + }, + { + code: ` + switch (foo) { + case 1: { + a(); + } + default: { + let a; + } + } + `, + languageOptions: { parserOptions }, + }, + { + code: ` + a(); + { + let a = function () {}; + } + `, + languageOptions: { parserOptions }, + }, + + // object style options + { + code: ` + a(); + function a() { + alert(arguments); + } + `, + options: [{ functions: false }], + }, + { + code: ` + 'use strict'; + { + a(); + function a() {} + } + `, + languageOptions: { parserOptions }, + options: [{ functions: false }], + }, + { + code: ` + function foo() { + new A(); + } + class A {} + `, + languageOptions: { parserOptions }, + options: [{ classes: false }], + }, + + // "variables" option + { + code: ` + function foo() { + bar; + } + var bar; + `, + options: [{ variables: false }], + }, + { + code: ` + var foo = () => bar; + var bar; + `, + languageOptions: { parserOptions }, + options: [{ variables: false }], + }, + + // "typedefs" option + { + code: ` + var x: Foo = 2; + type Foo = string | number; + `, + options: [{ typedefs: false }], + }, + // https://github.com/typescript-eslint/typescript-eslint/issues/2572 + // { + // code: ` + // interface Bar { + // type: typeof Foo; + // } + + // const Foo = 2; + // `, + // options: [{ ignoreTypeReferences: true }], + // }, + // { + // code: ` + // interface Bar { + // type: typeof Foo.FOO; + // } + + // class Foo { + // public static readonly FOO = ''; + // } + // `, + // options: [{ ignoreTypeReferences: true }], + // }, + // { + // code: ` + // interface Bar { + // type: typeof Foo.Bar.Baz; + // } + + // const Foo = { + // Bar: { + // Baz: 1, + // }, + // }; + // `, + // options: [{ ignoreTypeReferences: true }], + // }, + // https://github.com/bradzacher/eslint-plugin-typescript/issues/141 + { + code: ` + interface ITest { + first: boolean; + second: string; + third: boolean; + } + + let first = () => console.log('first'); + + export let second = () => console.log('second'); + + export namespace Third { + export let third = () => console.log('third'); + } + `, + languageOptions: { + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + }, + // https://github.com/eslint/typescript-eslint-parser/issues/550 + ` + function test(file: Blob) { + const slice: typeof file.slice = + file.slice || (file as any).webkitSlice || (file as any).mozSlice; + return slice; + } + `, + // https://github.com/eslint/typescript-eslint-parser/issues/435 + ` + interface Foo { + bar: string; + } + const bar = 'blah'; + `, + { + code: ` + function foo(): Foo { + return Foo.FOO; + } + + enum Foo { + FOO, + } + `, + options: [{ enums: false }], + }, + { + code: ` + let foo: Foo; + + enum Foo { + FOO, + } + `, + options: [{ enums: false }], + }, + { + code: ` + class Test { + foo(args: Foo): Foo { + return Foo.FOO; + } + } + + enum Foo { + FOO, + } + `, + options: [{ enums: false }], + }, + + // "allowNamedExports" option + { + code: ` + export { a }; + const a = 1; + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { a as b }; + const a = 1; + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { a, b }; + let a, b; + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { a }; + var a; + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { f }; + function f() {} + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { C }; + class C {} + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { Foo }; + + enum Foo { + BAR, + } + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { Foo }; + + namespace Foo { + export let bar = () => console.log('bar'); + } + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + // { + // code: ` + // export { Foo, baz }; + + // enum Foo { + // BAR, + // } + + // let baz: Enum; + // enum Enum {} + // `, + // languageOptions: { parserOptions }, + // options: [{ allowNamedExports: true }], + // }, + // https://github.com/typescript-eslint/typescript-eslint/issues/2502 + { + code: ` + import * as React from 'react'; + +
; + `, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + sourceType: 'module', + }, + }, + }, + { + code: ` + import React from 'react'; + +
; + `, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + sourceType: 'module', + }, + }, + }, + { + code: ` + import { h } from 'preact'; + +
; + `, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + jsxPragma: 'h', + sourceType: 'module', + }, + }, + }, + { + code: ` + const React = require('react'); + +
; + `, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + // https://github.com/typescript-eslint/typescript-eslint/issues/2527 + ` + type T = (value: unknown) => value is Id; + `, + ` + global.foo = true; + + declare global { + namespace NodeJS { + interface Global { + foo?: boolean; + } + } + } + `, + // https://github.com/typescript-eslint/typescript-eslint/issues/2824 + // ` + // @Directive({ + // selector: '[rcCidrIpPattern]', + // providers: [ + // { + // provide: NG_VALIDATORS, + // useExisting: CidrIpPatternDirective, + // multi: true, + // }, + // ], + // }) + // export class CidrIpPatternDirective implements Validator {} + // `, + // { + // code: ` + // @Directive({ + // selector: '[rcCidrIpPattern]', + // providers: [ + // { + // provide: NG_VALIDATORS, + // useExisting: CidrIpPatternDirective, + // multi: true, + // }, + // ], + // }) + // export class CidrIpPatternDirective implements Validator {} + // `, + // options: [ + // { + // classes: false, + // }, + // ], + // }, + // https://github.com/typescript-eslint/typescript-eslint/issues/2941 + ` + class A { + constructor(printName) { + this.printName = printName; + } + + openPort(printerName = this.printerName) { + this.tscOcx.ActiveXopenport(printerName); + + return this; + } + } + `, + // { + // code: ` + // const obj = { + // foo: 'foo-value', + // bar: 'bar-value', + // } satisfies { + // [key in 'foo' | 'bar']: \`\${key}-value\`; + // }; + // `, + // options: [{ ignoreTypeReferences: false }], + // }, + // { + // code: ` + // const obj = { + // foo: 'foo-value', + // bar: 'bar-value', + // } as { + // [key in 'foo' | 'bar']: \`\${key}-value\`; + // }; + // `, + // options: [{ ignoreTypeReferences: false }], + // }, + // { + // code: ` + // const obj = { + // foo: { + // foo: 'foo', + // } as { + // [key in 'foo' | 'bar']: key; + // }, + // }; + // `, + // options: [{ ignoreTypeReferences: false }], + // }, + // { + // code: ` + // const foo = { + // bar: 'bar', + // } satisfies { + // bar: typeof baz; + // }; + + // const baz = ''; + // `, + // options: [{ ignoreTypeReferences: true }], + // }, + // ` + // namespace A.X.Y {} + + // import Z = A.X.Y; + + // const X = 23; + // `, + ], + invalid: [ + { + code: ` + a++; + var a = 19; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { + parserOptions: { sourceType: 'module' }, + }, + }, + { + code: ` + a++; + var a = 19; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + a++; + var a = 19; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + }, + { + code: ` + a(); + var a = function () {}; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + }, + { + code: ` + alert(a[1]); + var a = [1, 3]; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + }, + { + code: ` + a(); + function a() { + alert(b); + var b = 10; + a(); + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + { + data: { name: 'b' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + }, + { + code: ` + a(); + var a = function () {}; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + options: ['nofunc'], + }, + { + code: ` + (() => { + alert(a); + var a = 42; + })(); + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + (() => a())(); + function a() {} + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + a(); + try { + throw new Error(); + } catch (foo) { + var a; + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + }, + { + code: ` + var f = () => a; + var a; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + new A(); + class A {} + `, + errors: [ + { + data: { name: 'A' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + function foo() { + new A(); + } + class A {} + `, + errors: [ + { + data: { name: 'A' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + new A(); + var A = class {}; + `, + errors: [ + { + data: { name: 'A' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + function foo() { + new A(); + } + var A = class {}; + `, + errors: [ + { + data: { name: 'A' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + + // Block-level bindings + { + code: ` + a++; + { + var a; + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + 'use strict'; + { + a(); + function a() {} + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + { + a; + let a = 1; + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + switch (foo) { + case 1: + a(); + default: + let a; + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + if (true) { + function foo() { + a; + } + let a; + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + + // object style options + { + code: ` + a(); + var a = function () {}; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + options: [{ classes: false, functions: false }], + }, + { + code: ` + new A(); + var A = class {}; + `, + errors: [ + { + data: { name: 'A' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + options: [{ classes: false }], + }, + { + code: ` + function foo() { + new A(); + } + var A = class {}; + `, + errors: [ + { + data: { name: 'A' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + options: [{ classes: false }], + }, + + // invalid initializers + { + code: 'var a = a;', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + }, + { + code: 'let a = a + b;', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'const a = foo(a);', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'function foo(a = a) {}', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'var { a = a } = [];', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'var [a = a] = [];', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'var { b = a, a } = {};', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'var [b = a, a] = {};', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'var { a = 0 } = a;', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: 'var [a = 0] = a;', + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + for (var a in a) { + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + }, + { + code: ` + for (var a of a) { + } + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + }, + + // "ignoreTypeReferences" option + { + code: ` + interface Bar { + type: typeof Foo; + } + + const Foo = 2; + `, + errors: [ + { + data: { name: 'Foo' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + options: [{ ignoreTypeReferences: false }], + }, + { + code: ` + interface Bar { + type: typeof Foo.FOO; + } + + class Foo { + public static readonly FOO = ''; + } + `, + errors: [ + { + data: { name: 'Foo' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + options: [{ ignoreTypeReferences: false }], + }, + { + code: ` + interface Bar { + type: typeof Foo.Bar.Baz; + } + + const Foo = { + Bar: { + Baz: 1, + }, + }; + `, + errors: [ + { + data: { name: 'Foo' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + options: [{ ignoreTypeReferences: false }], + }, + { + code: ` + const foo = { + bar: 'bar', + } satisfies { + bar: typeof baz; + }; + + const baz = ''; + `, + errors: [ + { + data: { name: 'baz' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + options: [{ ignoreTypeReferences: false }], + }, + + // "variables" option + { + code: ` + function foo() { + bar; + var bar = 1; + } + var bar; + `, + errors: [ + { + data: { name: 'bar' }, + messageId: 'usedBeforeDefined', + type: "Identifier", + }, + ], + languageOptions: { parserOptions }, + options: [{ variables: false }], + }, + // { + // code: ` + // class Test { + // foo(args: Foo): Foo { + // return Foo.FOO; + // } + // } + + // enum Foo { + // FOO, + // } + // `, + // errors: [ + // { + // data: { name: 'Foo' }, + // line: 4, + // messageId: 'usedBeforeDefined', + // }, + // ], + // options: [{ enums: true }], + // }, + // { + // code: ` + // function foo(): Foo { + // return Foo.FOO; + // } + + // enum Foo { + // FOO, + // } + // `, + // errors: [ + // { + // data: { name: 'Foo' }, + // line: 3, + // messageId: 'usedBeforeDefined', + // }, + // ], + // options: [{ enums: true }], + // }, + { + code: ` + const foo = Foo.Foo; + + enum Foo { + FOO, + } + `, + errors: [ + { + data: { name: 'Foo' }, + line: 2, + messageId: 'usedBeforeDefined', + }, + ], + options: [{ enums: true }], + }, + // "allowNamedExports" option + { + code: ` + export { a }; + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + export { a }; + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + options: [{}], + }, + { + code: ` + export { a }; + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + options: [{ allowNamedExports: false }], + }, + { + code: ` + export { a }; + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + options: ['nofunc'], + }, + { + code: ` + export { a as b }; + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + export { a, b }; + let a, b; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + { + data: { name: 'b' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + export { a }; + var a; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + export { f }; + function f() {} + `, + errors: [ + { + data: { name: 'f' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + export { C }; + class C {} + `, + errors: [ + { + data: { name: 'C' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + export const foo = a; + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export function foo() { + return a; + } + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export class C { + foo() { + return a; + } + } + const a = 1; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, + { + code: ` + export { Foo }; + + enum Foo { + BAR, + } + `, + errors: [ + { + data: { name: 'Foo' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + { + code: ` + export { Foo }; + + namespace Foo { + export let bar = () => console.log('bar'); + } + `, + errors: [ + { + data: { name: 'Foo' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + }, + // { + // code: ` + // export { Foo, baz }; + + // enum Foo { + // BAR, + // } + + // let baz: Enum; + // enum Enum {} + // `, + // errors: [ + // { + // data: { name: 'Foo' }, + // messageId: 'usedBeforeDefined', + // }, + // { + // data: { name: 'baz' }, + // messageId: 'usedBeforeDefined', + // }, + // ], + // languageOptions: { parserOptions }, + // options: [{ allowNamedExports: false, ignoreTypeReferences: true }], + // }, + { + code: ` + f(); + function f() {} + `, + errors: [ + { + data: { name: 'f' }, + messageId: 'usedBeforeDefined', + }, + ], + }, + { + code: ` + alert(a); + var a = 10; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + }, + { + code: ` + f()?.(); + function f() { + return function t() {}; + } + `, + errors: [ + { + data: { name: 'f' }, + messageId: 'usedBeforeDefined', + }, + ], + }, + { + code: ` + alert(a?.b); + var a = { b: 5 }; + `, + errors: [ + { + data: { name: 'a' }, + messageId: 'usedBeforeDefined', + }, + ], + }, + ], +}); From d7f6c64db49803691672f69862cf8cab0a2eedec Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Mon, 24 Mar 2025 13:18:14 +0530 Subject: [PATCH 2/9] update code and add docs --- docs/src/rules/no-use-before-define.md | 115 ++++++++ lib/rules/no-use-before-define.js | 61 ++++ tests/lib/rules/no-use-before-define.js | 366 ++++++++++++------------ 3 files changed, 359 insertions(+), 183 deletions(-) diff --git a/docs/src/rules/no-use-before-define.md b/docs/src/rules/no-use-before-define.md index 726f5fd861b6..50527d82952d 100644 --- a/docs/src/rules/no-use-before-define.md +++ b/docs/src/rules/no-use-before-define.md @@ -157,6 +157,18 @@ export { foo }; These references are safe even if the variables are declared later in the code. Default is `false`. +This rule additionally supports TypeScript type syntax. The following options enable checking for the references to `type`, `interface` and `enum` declarations: + +* `enums` (`boolean`) - + If it is `true`, the rule warns every reference to an `enum` before it is defined. + Defult is `true`. +* `typedefs` (`boolean`) - + If it is `true`, this rule warns every reference to a type `alias` or `interface` before its declaration. If `false`, the rule allows using type `alias`es and `interface`s before they are defined. + Default is `true`. +* `ignoreTypeReferences` (`boolean`) - + If it is `true`, rule will ignore all type references, such as in type annotations and assertions. + Default is `true`. + This rule accepts `"nofunc"` string as an option. `"nofunc"` is the same as `{ "functions": false, "classes": true, "variables": true, "allowNamedExports": false }`. @@ -350,3 +362,106 @@ const d = 1; ``` ::: + +### enums (TypeScript only) + +Examples of **incorrect** code for the `{ "enums": true }` option: + +::: incorrect + +```ts +/*eslint no-use-before-define: ["error", { "enums": true }]*/ + +const x = Foo.FOO; + +enum Foo { + FOO, +} +``` + +::: + +Examples of **correct** code for the `{ "enums": true }` option: + +::: correct + +```ts +/*eslint no-use-before-define: ["error", { "enums": true }]*/ + +enum Foo { + FOO, +} + +const x = Foo.FOO; +``` + +::: + +### typedefs (TypeScript only) + +Examples of **incorrect** code for the `{ "enums": true }` option: + +::: incorrect + +```ts +/*eslint no-use-before-define: ["error", { "typedefs": true }]*/ + +let myVar: StringOrNumber; + +type StringOrNumber = string | number; + +``` + +::: + +Examples of **correct** code for the `{ "typedefs": true }` option: + +::: correct + +```ts +/*eslint no-use-before-define: ["error", { "typedefs": true }]*/ + +type StringOrNumber = string | number; + +let myVar: StringOrNumber; +``` + +::: + +### ignoreTypeReferences (TypeScript only) + +Examples of **incorrect** code for the `{ "ignoreTypeReferences": false }` option: + +::: incorrect + +```ts +/*eslint no-use-before-define: ["error", { "ignoreTypeReferences": false }]*/ + +let var1: StringOrNumber; + +type StringOrNumber = string | number; + +let var2: Enum; + +enum Enum {} +``` + +::: + +Examples of **correct** code for the `{ "ignoreTypeReferences": false }` option: + +::: correct + +```ts +/*eslint no-use-before-define: ["error", { "ignoreTypeReferences": false }]*/ + +type StringOrNumber = string | number; + +let myVar: StringOrNumber; + +enum Enum {} + +let var2: Enum; +``` + +::: diff --git a/lib/rules/no-use-before-define.js b/lib/rules/no-use-before-define.js index 5305f6dd5dee..4605383ffa38 100644 --- a/lib/rules/no-use-before-define.js +++ b/lib/rules/no-use-before-define.js @@ -208,6 +208,55 @@ function isEvaluatedDuringInitialization(reference) { return false; } +/** + * check whether the reference contains a type query. + * @param {ASTNode} node Identifier node to check. + * @returns true if reference contains type query. + */ + +function referenceContainsTypeQuery(node) { + switch (node.type) { + case "TSTypeQuery": + return true; + + case "TSQualifiedName": + case "Identifier": + return referenceContainsTypeQuery(node.parent); + + default: + // if we find a different node, there's no chance that we're in a TSTypeQuery + return false; + } +} + +/** + * Decorators are transpiled such that the decorator is placed after the class declaration + * So it is considered safe + * @param {Variable} variable The variable to check. + * @param {Reference} reference The reference to check. + * @returns {boolean} `true` if the reference is in a class decorator. + */ +function isClassRefInClassDecorator(variable, reference) { + if ( + variable.defs[0].type !== "ClassName" || + variable.defs[0].node?.decorators?.length === 0 || + !Array.isArray(variable.defs[0].node?.decorators) + ) { + return false; + } + + for (const deco of variable.defs[0].node.decorators) { + if ( + reference.identifier.range[0] >= deco.range[0] && + reference.identifier.range[1] <= deco.range[1] + ) { + return true; + } + } + + return false; +} + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -324,6 +373,18 @@ module.exports = { return false; } + if ( + options.ignoreTypeReferences && + referenceContainsTypeQuery(identifier) || + identifier.parent.type === "TSTypeReference" + ) { + return false; + } + + if (isClassRefInClassDecorator(variable, reference)) { + return false; + } + return true; } diff --git a/tests/lib/rules/no-use-before-define.js b/tests/lib/rules/no-use-before-define.js index da4ccd37a288..f69cd7d59b8d 100644 --- a/tests/lib/rules/no-use-before-define.js +++ b/tests/lib/rules/no-use-before-define.js @@ -1897,42 +1897,42 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { options: [{ typedefs: false }], }, // https://github.com/typescript-eslint/typescript-eslint/issues/2572 - // { - // code: ` - // interface Bar { - // type: typeof Foo; - // } + { + code: ` + interface Bar { + type: typeof Foo; + } - // const Foo = 2; - // `, - // options: [{ ignoreTypeReferences: true }], - // }, - // { - // code: ` - // interface Bar { - // type: typeof Foo.FOO; - // } + const Foo = 2; + `, + options: [{ ignoreTypeReferences: true }], + }, + { + code: ` + interface Bar { + type: typeof Foo.FOO; + } - // class Foo { - // public static readonly FOO = ''; - // } - // `, - // options: [{ ignoreTypeReferences: true }], - // }, - // { - // code: ` - // interface Bar { - // type: typeof Foo.Bar.Baz; - // } + class Foo { + public static readonly FOO = ''; + } + `, + options: [{ ignoreTypeReferences: true }], + }, + { + code: ` + interface Bar { + type: typeof Foo.Bar.Baz; + } - // const Foo = { - // Bar: { - // Baz: 1, - // }, - // }; - // `, - // options: [{ ignoreTypeReferences: true }], - // }, + const Foo = { + Bar: { + Baz: 1, + }, + }; + `, + options: [{ ignoreTypeReferences: true }], + }, // https://github.com/bradzacher/eslint-plugin-typescript/issues/141 { code: ` @@ -2077,20 +2077,20 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { languageOptions: { parserOptions }, options: [{ allowNamedExports: true }], }, - // { - // code: ` - // export { Foo, baz }; + { + code: ` + export { Foo, baz }; - // enum Foo { - // BAR, - // } + enum Foo { + BAR, + } - // let baz: Enum; - // enum Enum {} - // `, - // languageOptions: { parserOptions }, - // options: [{ allowNamedExports: true }], - // }, + let baz: Enum; + enum Enum {} + `, + languageOptions: { parserOptions }, + options: [{ allowNamedExports: true }], + }, // https://github.com/typescript-eslint/typescript-eslint/issues/2502 { code: ` @@ -2168,39 +2168,39 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { } `, // https://github.com/typescript-eslint/typescript-eslint/issues/2824 - // ` - // @Directive({ - // selector: '[rcCidrIpPattern]', - // providers: [ - // { - // provide: NG_VALIDATORS, - // useExisting: CidrIpPatternDirective, - // multi: true, - // }, - // ], - // }) - // export class CidrIpPatternDirective implements Validator {} - // `, - // { - // code: ` - // @Directive({ - // selector: '[rcCidrIpPattern]', - // providers: [ - // { - // provide: NG_VALIDATORS, - // useExisting: CidrIpPatternDirective, - // multi: true, - // }, - // ], - // }) - // export class CidrIpPatternDirective implements Validator {} - // `, - // options: [ - // { - // classes: false, - // }, - // ], - // }, + ` + @Directive({ + selector: '[rcCidrIpPattern]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: CidrIpPatternDirective, + multi: true, + }, + ], + }) + export class CidrIpPatternDirective implements Validator {} + `, + { + code: ` + @Directive({ + selector: '[rcCidrIpPattern]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: CidrIpPatternDirective, + multi: true, + }, + ], + }) + export class CidrIpPatternDirective implements Validator {} + `, + options: [ + { + classes: false, + }, + ], + }, // https://github.com/typescript-eslint/typescript-eslint/issues/2941 ` class A { @@ -2215,52 +2215,52 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { } } `, - // { - // code: ` - // const obj = { - // foo: 'foo-value', - // bar: 'bar-value', - // } satisfies { - // [key in 'foo' | 'bar']: \`\${key}-value\`; - // }; - // `, - // options: [{ ignoreTypeReferences: false }], - // }, - // { - // code: ` - // const obj = { - // foo: 'foo-value', - // bar: 'bar-value', - // } as { - // [key in 'foo' | 'bar']: \`\${key}-value\`; - // }; - // `, - // options: [{ ignoreTypeReferences: false }], - // }, - // { - // code: ` - // const obj = { - // foo: { - // foo: 'foo', - // } as { - // [key in 'foo' | 'bar']: key; - // }, - // }; - // `, - // options: [{ ignoreTypeReferences: false }], - // }, - // { - // code: ` - // const foo = { - // bar: 'bar', - // } satisfies { - // bar: typeof baz; - // }; + { + code: ` + const obj = { + foo: 'foo-value', + bar: 'bar-value', + } satisfies { + [key in 'foo' | 'bar']: \`\${key}-value\`; + }; + `, + options: [{ ignoreTypeReferences: false }], + }, + { + code: ` + const obj = { + foo: 'foo-value', + bar: 'bar-value', + } as { + [key in 'foo' | 'bar']: \`\${key}-value\`; + }; + `, + options: [{ ignoreTypeReferences: false }], + }, + { + code: ` + const obj = { + foo: { + foo: 'foo', + } as { + [key in 'foo' | 'bar']: key; + }, + }; + `, + options: [{ ignoreTypeReferences: false }], + }, + { + code: ` + const foo = { + bar: 'bar', + } satisfies { + bar: typeof baz; + }; - // const baz = ''; - // `, - // options: [{ ignoreTypeReferences: true }], - // }, + const baz = ''; + `, + options: [{ ignoreTypeReferences: true }], + }, // ` // namespace A.X.Y {} @@ -2867,46 +2867,46 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { languageOptions: { parserOptions }, options: [{ variables: false }], }, - // { - // code: ` - // class Test { - // foo(args: Foo): Foo { - // return Foo.FOO; - // } - // } + { + code: ` + class Test { + foo(args: Foo): Foo { + return Foo.FOO; + } + } - // enum Foo { - // FOO, - // } - // `, - // errors: [ - // { - // data: { name: 'Foo' }, - // line: 4, - // messageId: 'usedBeforeDefined', - // }, - // ], - // options: [{ enums: true }], - // }, - // { - // code: ` - // function foo(): Foo { - // return Foo.FOO; - // } + enum Foo { + FOO, + } + `, + errors: [ + { + data: { name: 'Foo' }, + line: 4, + messageId: 'usedBeforeDefined', + }, + ], + options: [{ enums: true }], + }, + { + code: ` + function foo(): Foo { + return Foo.FOO; + } - // enum Foo { - // FOO, - // } - // `, - // errors: [ - // { - // data: { name: 'Foo' }, - // line: 3, - // messageId: 'usedBeforeDefined', - // }, - // ], - // options: [{ enums: true }], - // }, + enum Foo { + FOO, + } + `, + errors: [ + { + data: { name: 'Foo' }, + line: 3, + messageId: 'usedBeforeDefined', + }, + ], + options: [{ enums: true }], + }, { code: ` const foo = Foo.Foo; @@ -3129,30 +3129,30 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { ], languageOptions: { parserOptions }, }, - // { - // code: ` - // export { Foo, baz }; + { + code: ` + export { Foo, baz }; - // enum Foo { - // BAR, - // } + enum Foo { + BAR, + } - // let baz: Enum; - // enum Enum {} - // `, - // errors: [ - // { - // data: { name: 'Foo' }, - // messageId: 'usedBeforeDefined', - // }, - // { - // data: { name: 'baz' }, - // messageId: 'usedBeforeDefined', - // }, - // ], - // languageOptions: { parserOptions }, - // options: [{ allowNamedExports: false, ignoreTypeReferences: true }], - // }, + let baz: Enum; + enum Enum {} + `, + errors: [ + { + data: { name: 'Foo' }, + messageId: 'usedBeforeDefined', + }, + { + data: { name: 'baz' }, + messageId: 'usedBeforeDefined', + }, + ], + languageOptions: { parserOptions }, + options: [{ allowNamedExports: false, ignoreTypeReferences: true }], + }, { code: ` f(); From d842554126a2bcb388383238e09f92b3746f8ac1 Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Thu, 27 Mar 2025 18:56:30 +0530 Subject: [PATCH 3/9] fix linting errors --- lib/rules/no-use-before-define.js | 64 +- tests/lib/rules/no-use-before-define.js | 1415 ++++++++++++----------- 2 files changed, 759 insertions(+), 720 deletions(-) diff --git a/lib/rules/no-use-before-define.js b/lib/rules/no-use-before-define.js index 4605383ffa38..81da802d06bc 100644 --- a/lib/rules/no-use-before-define.js +++ b/lib/rules/no-use-before-define.js @@ -30,6 +30,9 @@ function parseOptions(options) { classes: true, variables: true, allowNamedExports: false, + enums: true, + typedefs: true, + ignoreTypeReferences: true, }; } @@ -211,21 +214,20 @@ function isEvaluatedDuringInitialization(reference) { /** * check whether the reference contains a type query. * @param {ASTNode} node Identifier node to check. - * @returns true if reference contains type query. + * @returns {boolean} true if reference contains type query. */ - function referenceContainsTypeQuery(node) { switch (node.type) { - case "TSTypeQuery": - return true; - - case "TSQualifiedName": - case "Identifier": - return referenceContainsTypeQuery(node.parent); - - default: - // if we find a different node, there's no chance that we're in a TSTypeQuery - return false; + case "TSTypeQuery": + return true; + + case "TSQualifiedName": + case "Identifier": + return referenceContainsTypeQuery(node.parent); + + default: + // if we find a different node, there's no chance that we're in a TSTypeQuery + return false; } } @@ -238,22 +240,22 @@ function referenceContainsTypeQuery(node) { */ function isClassRefInClassDecorator(variable, reference) { if ( - variable.defs[0].type !== "ClassName" || - variable.defs[0].node?.decorators?.length === 0 || - !Array.isArray(variable.defs[0].node?.decorators) + variable.defs[0].type !== "ClassName" || + variable.defs[0].node?.decorators?.length === 0 || + !Array.isArray(variable.defs[0].node?.decorators) ) { - return false; + return false; } for (const deco of variable.defs[0].node.decorators) { if ( - reference.identifier.range[0] >= deco.range[0] && - reference.identifier.range[1] <= deco.range[1] + reference.identifier.range[0] >= deco.range[0] && + reference.identifier.range[1] <= deco.range[1] ) { - return true; + return true; } } - + return false; } @@ -375,12 +377,27 @@ module.exports = { if ( options.ignoreTypeReferences && - referenceContainsTypeQuery(identifier) || - identifier.parent.type === "TSTypeReference" + (referenceContainsTypeQuery(identifier) || + identifier.parent.type === "TSTypeReference") ) { return false; } + // skip nested namespace aliases as variable references + if (identifier.parent.type === "TSQualifiedName") { + let currentNode = identifier.parent; + + while (currentNode.type === "TSQualifiedName") { + currentNode = currentNode.left; + } + + if (currentNode === identifier) { + return true; + } + + return false; + } + if (isClassRefInClassDecorator(variable, reference)) { return false; } @@ -401,7 +418,8 @@ module.exports = { if ( reference.identifier.range[1] < definitionIdentifier.range[1] || - isEvaluatedDuringInitialization(reference) + (isEvaluatedDuringInitialization(reference) && + reference.identifier.parent.type !== "TSTypeReference") ) { context.report({ node: reference.identifier, diff --git a/tests/lib/rules/no-use-before-define.js b/tests/lib/rules/no-use-before-define.js index f69cd7d59b8d..6a24ac3af141 100644 --- a/tests/lib/rules/no-use-before-define.js +++ b/tests/lib/rules/no-use-before-define.js @@ -1708,19 +1708,19 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { alert(a); } `, - 'Object.hasOwnProperty.call(a);', + "Object.hasOwnProperty.call(a);", ` function a() { alert(arguments); } `, - 'declare function a();', + "declare function a();", ` declare class a { foo(); } `, - 'const updatedAt = data?.updatedAt;', + "const updatedAt = data?.updatedAt;", ` function f() { return function t() {}; @@ -1732,22 +1732,22 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { alert(a?.b); `, { - code: ` + code: ` a(); function a() { alert(arguments); } `, - options: ['nofunc'], + options: ["nofunc"], }, { - code: ` + code: ` (() => { var a = 42; alert(a); })(); `, - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, }, ` a(); @@ -1756,18 +1756,24 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { } catch (a) {} `, { - code: ` + code: ` class A {} new A(); `, - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, }, ` var a = 0, b = a; `, - { code: 'var { a = 0, b = a } = {};', languageOptions: { parserOptions } }, - { code: 'var [a = 0, b = a] = {};', languageOptions: { parserOptions } }, + { + code: "var { a = 0, b = a } = {};", + languageOptions: { parserOptions }, + }, + { + code: "var [a = 0, b = a] = {};", + languageOptions: { parserOptions }, + }, ` function foo() { foo(); @@ -1784,38 +1790,38 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { } `, { - code: ` + code: ` var a; for (a of a) { } `, - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, }, - + // Block-level bindings { - code: ` + code: ` 'use strict'; a(); { function a() {} } `, - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, }, { - code: ` + code: ` 'use strict'; { a(); function a() {} } `, - languageOptions: { parserOptions }, - options: ['nofunc'], + options: ["nofunc"], + languageOptions: { parserOptions }, }, { - code: ` + code: ` switch (foo) { case 1: { a(); @@ -1825,90 +1831,90 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { } } `, - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, }, { - code: ` + code: ` a(); { let a = function () {}; } `, - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, }, - + // object style options { - code: ` + code: ` a(); function a() { alert(arguments); } `, - options: [{ functions: false }], + options: [{ functions: false }], }, { - code: ` + code: ` 'use strict'; { a(); function a() {} } `, - languageOptions: { parserOptions }, - options: [{ functions: false }], + options: [{ functions: false }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` function foo() { new A(); } class A {} `, - languageOptions: { parserOptions }, - options: [{ classes: false }], + options: [{ classes: false }], + languageOptions: { parserOptions }, }, - + // "variables" option { - code: ` + code: ` function foo() { bar; } var bar; `, - options: [{ variables: false }], + options: [{ variables: false }], }, { - code: ` + code: ` var foo = () => bar; var bar; `, - languageOptions: { parserOptions }, - options: [{ variables: false }], + options: [{ variables: false }], + languageOptions: { parserOptions }, }, - + // "typedefs" option { - code: ` + code: ` var x: Foo = 2; type Foo = string | number; `, - options: [{ typedefs: false }], + options: [{ typedefs: false }], }, // https://github.com/typescript-eslint/typescript-eslint/issues/2572 { - code: ` + code: ` interface Bar { type: typeof Foo; } const Foo = 2; `, - options: [{ ignoreTypeReferences: true }], + options: [{ ignoreTypeReferences: true }], }, { - code: ` + code: ` interface Bar { type: typeof Foo.FOO; } @@ -1917,10 +1923,10 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { public static readonly FOO = ''; } `, - options: [{ ignoreTypeReferences: true }], + options: [{ ignoreTypeReferences: true }], }, { - code: ` + code: ` interface Bar { type: typeof Foo.Bar.Baz; } @@ -1931,11 +1937,11 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { }, }; `, - options: [{ ignoreTypeReferences: true }], + options: [{ ignoreTypeReferences: true }], }, // https://github.com/bradzacher/eslint-plugin-typescript/issues/141 { - code: ` + code: ` interface ITest { first: boolean; second: string; @@ -1950,9 +1956,9 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { export let third = () => console.log('third'); } `, - languageOptions: { - parserOptions: { ecmaVersion: 6, sourceType: 'module' }, - }, + languageOptions: { + parserOptions: { ecmaVersion: 6, sourceType: "module" }, + }, }, // https://github.com/eslint/typescript-eslint-parser/issues/550 ` @@ -1970,7 +1976,7 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { const bar = 'blah'; `, { - code: ` + code: ` function foo(): Foo { return Foo.FOO; } @@ -1979,20 +1985,20 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { FOO, } `, - options: [{ enums: false }], + options: [{ enums: false }], }, { - code: ` + code: ` let foo: Foo; enum Foo { FOO, } `, - options: [{ enums: false }], + options: [{ enums: false }], }, { - code: ` + code: ` class Test { foo(args: Foo): Foo { return Foo.FOO; @@ -2003,82 +2009,82 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { FOO, } `, - options: [{ enums: false }], + options: [{ enums: false }], }, - + // "allowNamedExports" option { - code: ` + code: ` export { a }; const a = 1; `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { a as b }; const a = 1; `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { a, b }; let a, b; `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { a }; var a; `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { f }; function f() {} `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { C }; class C {} `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { Foo }; enum Foo { BAR, } `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { Foo }; namespace Foo { export let bar = () => console.log('bar'); } `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, { - code: ` + code: ` export { Foo, baz }; enum Foo { @@ -2088,69 +2094,69 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { let baz: Enum; enum Enum {} `, - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, }, // https://github.com/typescript-eslint/typescript-eslint/issues/2502 { - code: ` + code: ` import * as React from 'react';
; `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - sourceType: 'module', + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + sourceType: "module", + }, }, - }, }, { - code: ` + code: ` import React from 'react';
; `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - sourceType: 'module', + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + sourceType: "module", + }, }, - }, }, { - code: ` + code: ` import { h } from 'preact';
; `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - jsxPragma: 'h', - sourceType: 'module', + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + jsxPragma: "h", + sourceType: "module", + }, }, - }, }, { - code: ` + code: ` const React = require('react');
; `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, }, - }, }, // https://github.com/typescript-eslint/typescript-eslint/issues/2527 ` @@ -2182,7 +2188,7 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { export class CidrIpPatternDirective implements Validator {} `, { - code: ` + code: ` @Directive({ selector: '[rcCidrIpPattern]', providers: [ @@ -2195,11 +2201,11 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { }) export class CidrIpPatternDirective implements Validator {} `, - options: [ - { - classes: false, - }, - ], + options: [ + { + classes: false, + }, + ], }, // https://github.com/typescript-eslint/typescript-eslint/issues/2941 ` @@ -2216,7 +2222,7 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { } `, { - code: ` + code: ` const obj = { foo: 'foo-value', bar: 'bar-value', @@ -2224,10 +2230,10 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { [key in 'foo' | 'bar']: \`\${key}-value\`; }; `, - options: [{ ignoreTypeReferences: false }], + options: [{ ignoreTypeReferences: false }], }, { - code: ` + code: ` const obj = { foo: 'foo-value', bar: 'bar-value', @@ -2235,10 +2241,10 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { [key in 'foo' | 'bar']: \`\${key}-value\`; }; `, - options: [{ ignoreTypeReferences: false }], + options: [{ ignoreTypeReferences: false }], }, { - code: ` + code: ` const obj = { foo: { foo: 'foo', @@ -2247,10 +2253,10 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { }, }; `, - options: [{ ignoreTypeReferences: false }], + options: [{ ignoreTypeReferences: false }], }, { - code: ` + code: ` const foo = { bar: 'bar', } satisfies { @@ -2259,88 +2265,88 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { const baz = ''; `, - options: [{ ignoreTypeReferences: true }], + options: [{ ignoreTypeReferences: true }], }, - // ` - // namespace A.X.Y {} + ` + namespace A.X.Y {} - // import Z = A.X.Y; + import Z = A.X.Y; - // const X = 23; - // `, - ], - invalid: [ + const X = 23; + `, + ], + invalid: [ { - code: ` + code: ` a++; var a = 19; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", + languageOptions: { + parserOptions: { sourceType: "module" }, }, - ], - languageOptions: { - parserOptions: { sourceType: 'module' }, - }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` a++; var a = 19; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` a++; var a = 19; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` a(); var a = function () {}; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` alert(a[1]); var a = [1, 3]; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` a(); function a() { alert(b); @@ -2348,65 +2354,65 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { a(); } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - { - data: { name: 'b' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + { + data: { name: "b" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` a(); var a = function () {}; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - options: ['nofunc'], + options: ["nofunc"], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` (() => { alert(a); var a = 42; })(); `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` (() => a())(); function a() {} `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` a(); try { throw new Error(); @@ -2414,141 +2420,141 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { var a; } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` var f = () => a; var a; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` new A(); class A {} `, - errors: [ - { - data: { name: 'A' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "A" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` function foo() { new A(); } class A {} `, - errors: [ - { - data: { name: 'A' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "A" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` new A(); var A = class {}; `, - errors: [ - { - data: { name: 'A' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "A" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` function foo() { new A(); } var A = class {}; `, - errors: [ - { - data: { name: 'A' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "A" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, - + // Block-level bindings { - code: ` + code: ` a++; { var a; } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` 'use strict'; { a(); function a() {} } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` { a; let a = 1; } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` switch (foo) { case 1: a(); @@ -2556,17 +2562,17 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { let a; } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` if (true) { function foo() { a; @@ -2574,222 +2580,237 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { let a; } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, - + // object style options { - code: ` + code: ` a(); var a = function () {}; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - options: [{ classes: false, functions: false }], + options: [{ classes: false, functions: false }], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` new A(); var A = class {}; `, - errors: [ - { - data: { name: 'A' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, - options: [{ classes: false }], + options: [{ classes: false }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "A" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` function foo() { new A(); } var A = class {}; `, - errors: [ - { - data: { name: 'A' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, - options: [{ classes: false }], + options: [{ classes: false }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "A" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, - + // invalid initializers { - code: 'var a = a;', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - }, - { - code: 'let a = a + b;', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "var a = a;", + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'const a = foo(a);', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "let a = a + b;", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'function foo(a = a) {}', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "const a = foo(a);", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'var { a = a } = [];', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "function foo(a = a) {}", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'var [a = a] = [];', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "var { a = a } = [];", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'var { b = a, a } = {};', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "var [a = a] = [];", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'var [b = a, a] = {};', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "var { b = a, a } = {};", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'var { a = 0 } = a;', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "var [b = a, a] = {};", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: 'var [a = 0] = a;', - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + code: "var { a = 0 } = a;", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: "var [a = 0] = a;", + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], + }, + { + code: ` for (var a in a) { } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` for (var a of a) { } `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, - + // "ignoreTypeReferences" option { - code: ` + code: ` interface Bar { type: typeof Foo; } const Foo = 2; `, - errors: [ - { - data: { name: 'Foo' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - options: [{ ignoreTypeReferences: false }], + options: [{ ignoreTypeReferences: false }], + errors: [ + { + data: { name: "Foo" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` + let var1: StringOrNumber; + +type StringOrNumber = string | number; + `, + options: [{ ignoreTypeReferences: false, typedefs: true }], + errors: [ + { + data: { name: "StringOrNumber" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], + }, + { + code: ` interface Bar { type: typeof Foo.FOO; } @@ -2798,17 +2819,17 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { public static readonly FOO = ''; } `, - errors: [ - { - data: { name: 'Foo' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - options: [{ ignoreTypeReferences: false }], + options: [{ ignoreTypeReferences: false }], + errors: [ + { + data: { name: "Foo" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` interface Bar { type: typeof Foo.Bar.Baz; } @@ -2819,17 +2840,17 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { }, }; `, - errors: [ - { - data: { name: 'Foo' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - options: [{ ignoreTypeReferences: false }], + options: [{ ignoreTypeReferences: false }], + errors: [ + { + data: { name: "Foo" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` const foo = { bar: 'bar', } satisfies { @@ -2838,37 +2859,37 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { const baz = ''; `, - errors: [ - { - data: { name: 'baz' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - options: [{ ignoreTypeReferences: false }], + options: [{ ignoreTypeReferences: false }], + errors: [ + { + data: { name: "baz" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, - + // "variables" option { - code: ` + code: ` function foo() { bar; var bar = 1; } var bar; `, - errors: [ - { - data: { name: 'bar' }, - messageId: 'usedBeforeDefined', - type: "Identifier", - }, - ], - languageOptions: { parserOptions }, - options: [{ variables: false }], + options: [{ variables: false }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "bar" }, + messageId: "usedBeforeDefined", + type: "Identifier", + }, + ], }, { - code: ` + code: ` class Test { foo(args: Foo): Foo { return Foo.FOO; @@ -2879,17 +2900,17 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { FOO, } `, - errors: [ - { - data: { name: 'Foo' }, - line: 4, - messageId: 'usedBeforeDefined', - }, - ], - options: [{ enums: true }], + options: [{ enums: true }], + errors: [ + { + data: { name: "Foo" }, + line: 4, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` function foo(): Foo { return Foo.FOO; } @@ -2898,189 +2919,189 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { FOO, } `, - errors: [ - { - data: { name: 'Foo' }, - line: 3, - messageId: 'usedBeforeDefined', - }, - ], - options: [{ enums: true }], + options: [{ enums: true }], + errors: [ + { + data: { name: "Foo" }, + line: 3, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` const foo = Foo.Foo; enum Foo { FOO, } `, - errors: [ - { - data: { name: 'Foo' }, - line: 2, - messageId: 'usedBeforeDefined', - }, - ], - options: [{ enums: true }], + options: [{ enums: true }], + errors: [ + { + data: { name: "Foo" }, + line: 2, + messageId: "usedBeforeDefined", + }, + ], }, // "allowNamedExports" option { - code: ` + code: ` export { a }; const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { a }; const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, - options: [{}], + options: [{}], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { a }; const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, - options: [{ allowNamedExports: false }], + options: [{ allowNamedExports: false }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { a }; const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, - options: ['nofunc'], + options: ["nofunc"], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { a as b }; const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { a, b }; let a, b; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - { - data: { name: 'b' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + { + data: { name: "b" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { a }; var a; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { f }; function f() {} `, - errors: [ - { - data: { name: 'f' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "f" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { C }; class C {} `, - errors: [ - { - data: { name: 'C' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "C" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export const foo = a; const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export function foo() { return a; } const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export class C { foo() { return a; @@ -3088,49 +3109,49 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { } const a = 1; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, - options: [{ allowNamedExports: true }], + options: [{ allowNamedExports: true }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { Foo }; enum Foo { BAR, } `, - errors: [ - { - data: { name: 'Foo' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "Foo" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { Foo }; namespace Foo { export let bar = () => console.log('bar'); } `, - errors: [ - { - data: { name: 'Foo' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "Foo" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` export { Foo, baz }; enum Foo { @@ -3140,68 +3161,68 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { let baz: Enum; enum Enum {} `, - errors: [ - { - data: { name: 'Foo' }, - messageId: 'usedBeforeDefined', - }, - { - data: { name: 'baz' }, - messageId: 'usedBeforeDefined', - }, - ], - languageOptions: { parserOptions }, - options: [{ allowNamedExports: false, ignoreTypeReferences: true }], + options: [{ allowNamedExports: false, ignoreTypeReferences: true }], + languageOptions: { parserOptions }, + errors: [ + { + data: { name: "Foo" }, + messageId: "usedBeforeDefined", + }, + { + data: { name: "baz" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` f(); function f() {} `, - errors: [ - { - data: { name: 'f' }, - messageId: 'usedBeforeDefined', - }, - ], + errors: [ + { + data: { name: "f" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` alert(a); var a = 10; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` f()?.(); function f() { return function t() {}; } `, - errors: [ - { - data: { name: 'f' }, - messageId: 'usedBeforeDefined', - }, - ], + errors: [ + { + data: { name: "f" }, + messageId: "usedBeforeDefined", + }, + ], }, { - code: ` + code: ` alert(a?.b); var a = { b: 5 }; `, - errors: [ - { - data: { name: 'a' }, - messageId: 'usedBeforeDefined', - }, - ], + errors: [ + { + data: { name: "a" }, + messageId: "usedBeforeDefined", + }, + ], }, ], }); From 73b3575bd1478857cd518897be42fd085bef2de8 Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Fri, 4 Apr 2025 13:16:20 +0530 Subject: [PATCH 4/9] update docs to fix CI error --- docs/src/rules/no-use-before-define.md | 31 ++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/docs/src/rules/no-use-before-define.md b/docs/src/rules/no-use-before-define.md index 50527d82952d..062b9dd743b5 100644 --- a/docs/src/rules/no-use-before-define.md +++ b/docs/src/rules/no-use-before-define.md @@ -399,31 +399,38 @@ const x = Foo.FOO; ### typedefs (TypeScript only) -Examples of **incorrect** code for the `{ "enums": true }` option: +Examples of **incorrect** code for the `{ "enums": true }` with `{ "ignoreTypeReferences": false }` option: ::: incorrect ```ts -/*eslint no-use-before-define: ["error", { "typedefs": true }]*/ +/*eslint no-use-before-define: ["error", { "typedefs": true, "ignoreTypeReferences": false }]*/ let myVar: StringOrNumber; type StringOrNumber = string | number; +const x: Foo = {}; + +interface Foo {} ``` ::: -Examples of **correct** code for the `{ "typedefs": true }` option: +Examples of **correct** code for the `{ "typedefs": true }` with `{ "ignoreTypeReferences": false }` option: ::: correct ```ts -/*eslint no-use-before-define: ["error", { "typedefs": true }]*/ +/*eslint no-use-before-define: ["error", { "typedefs": true, "ignoreTypeReferences": false }]*/ type StringOrNumber = string | number; let myVar: StringOrNumber; + +interface Foo {} + +const x: Foo = {}; ``` ::: @@ -464,4 +471,20 @@ enum Enum {} let var2: Enum; ``` +Examples of **correct** code for the `{ "ignoreTypeReferences": false }` with `{ "typedefs": false }` option: + +::: correct + +```ts +/*eslint no-use-before-define: ["error", { "ignoreTypeReferences": false, "typedefs": false, }]*/ + +let myVar: StringOrNumber; + +type StringOrNumber = string | number; + +const x: Foo = {}; + +interface Foo {} +``` + ::: From dda9f221a407b5f290ecae6764e9c44157cfb4ee Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Wed, 9 Apr 2025 17:21:18 +0530 Subject: [PATCH 5/9] apply some suggestions --- tests/lib/rules/no-use-before-define.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/lib/rules/no-use-before-define.js b/tests/lib/rules/no-use-before-define.js index 6a24ac3af141..841667a3f87f 100644 --- a/tests/lib/rules/no-use-before-define.js +++ b/tests/lib/rules/no-use-before-define.js @@ -1902,6 +1902,13 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { `, options: [{ typedefs: false }], }, + { + code: ` + var x: Foo = {}; + interface Foo {} + `, + options: [{ typedefs: false, ignoreTypeReferences: false, }], + }, // https://github.com/typescript-eslint/typescript-eslint/issues/2572 { code: ` @@ -2274,6 +2281,19 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { const X = 23; `, + ` + namespace A { + export namespace X { + export namespace Y { + export const foo = 40; + } + } + } + + import Z = A.X.Y; + + const X = 23; + ` ], invalid: [ { From 64659f89c4684ee033efbcf0c6226d1f912b05f2 Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Fri, 11 Apr 2025 12:19:06 +0530 Subject: [PATCH 6/9] add tests --- lib/rules/no-use-before-define.js | 9 ++++++--- tests/lib/rules/no-use-before-define.js | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/rules/no-use-before-define.js b/lib/rules/no-use-before-define.js index 81da802d06bc..6016a0b30693 100644 --- a/lib/rules/no-use-before-define.js +++ b/lib/rules/no-use-before-define.js @@ -239,10 +239,13 @@ function referenceContainsTypeQuery(node) { * @returns {boolean} `true` if the reference is in a class decorator. */ function isClassRefInClassDecorator(variable, reference) { + if (variable.defs[0].type !== 'ClassName') { + return false; + } + if ( - variable.defs[0].type !== "ClassName" || - variable.defs[0].node?.decorators?.length === 0 || - !Array.isArray(variable.defs[0].node?.decorators) + !variable.defs[0].node.decorators || + variable.defs[0].node.decorators.length === 0 ) { return false; } diff --git a/tests/lib/rules/no-use-before-define.js b/tests/lib/rules/no-use-before-define.js index 841667a3f87f..26c727168a02 100644 --- a/tests/lib/rules/no-use-before-define.js +++ b/tests/lib/rules/no-use-before-define.js @@ -1909,6 +1909,13 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { `, options: [{ typedefs: false, ignoreTypeReferences: false, }], }, + { + code: ` + let myVar: String; + type String = string; + `, + options: [{ typedefs: false, ignoreTypeReferences: false, }], + }, // https://github.com/typescript-eslint/typescript-eslint/issues/2572 { code: ` @@ -2214,6 +2221,21 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { }, ], }, + { + code: ` + @decorator + class C { + static x = "foo"; + [C.x]() { } + } + `, + errors: [ + { + data: { name: "C" }, + messageId: "usedBeforeDefined", + }, + ], + }, // https://github.com/typescript-eslint/typescript-eslint/issues/2941 ` class A { From b70fcc124e99ea2f3389d2a2ed6f1714e51a533e Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Fri, 11 Apr 2025 12:20:51 +0530 Subject: [PATCH 7/9] fix formating --- lib/rules/no-use-before-define.js | 4 ++-- tests/lib/rules/no-use-before-define.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rules/no-use-before-define.js b/lib/rules/no-use-before-define.js index 6016a0b30693..62631fcee9f7 100644 --- a/lib/rules/no-use-before-define.js +++ b/lib/rules/no-use-before-define.js @@ -239,10 +239,10 @@ function referenceContainsTypeQuery(node) { * @returns {boolean} `true` if the reference is in a class decorator. */ function isClassRefInClassDecorator(variable, reference) { - if (variable.defs[0].type !== 'ClassName') { + if (variable.defs[0].type !== "ClassName") { return false; } - + if ( !variable.defs[0].node.decorators || variable.defs[0].node.decorators.length === 0 diff --git a/tests/lib/rules/no-use-before-define.js b/tests/lib/rules/no-use-before-define.js index 26c727168a02..b24786a0689c 100644 --- a/tests/lib/rules/no-use-before-define.js +++ b/tests/lib/rules/no-use-before-define.js @@ -1907,14 +1907,14 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { var x: Foo = {}; interface Foo {} `, - options: [{ typedefs: false, ignoreTypeReferences: false, }], + options: [{ typedefs: false, ignoreTypeReferences: false }], }, { code: ` let myVar: String; type String = string; `, - options: [{ typedefs: false, ignoreTypeReferences: false, }], + options: [{ typedefs: false, ignoreTypeReferences: false }], }, // https://github.com/typescript-eslint/typescript-eslint/issues/2572 { @@ -2315,7 +2315,7 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { import Z = A.X.Y; const X = 23; - ` + `, ], invalid: [ { From d02b75e7d0471a878f74dd4c7bbadaee016da300 Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Fri, 11 Apr 2025 12:26:55 +0530 Subject: [PATCH 8/9] fix CI --- tests/lib/rules/no-use-before-define.js | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/lib/rules/no-use-before-define.js b/tests/lib/rules/no-use-before-define.js index b24786a0689c..3d6e0a65cbb1 100644 --- a/tests/lib/rules/no-use-before-define.js +++ b/tests/lib/rules/no-use-before-define.js @@ -2221,21 +2221,6 @@ ruleTesterTypeScript.run("no-use-before-define", rule, { }, ], }, - { - code: ` - @decorator - class C { - static x = "foo"; - [C.x]() { } - } - `, - errors: [ - { - data: { name: "C" }, - messageId: "usedBeforeDefined", - }, - ], - }, // https://github.com/typescript-eslint/typescript-eslint/issues/2941 ` class A { @@ -3266,5 +3251,20 @@ type StringOrNumber = string | number; }, ], }, + { + code: ` + @decorator + class C { + static x = "foo"; + [C.x]() { } + } + `, + errors: [ + { + data: { name: "C" }, + messageId: "usedBeforeDefined", + }, + ], + }, ], }); From 185c537ecaed7304d8225dda6f1bea60a1bb141a Mon Sep 17 00:00:00 2001 From: Tanuj Kanti Date: Thu, 29 May 2025 12:27:22 +0530 Subject: [PATCH 9/9] update types --- lib/types/rules.d.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/types/rules.d.ts b/lib/types/rules.d.ts index 27b9ce1dbdf7..e24328cf6196 100644 --- a/lib/types/rules.d.ts +++ b/lib/types/rules.d.ts @@ -4018,6 +4018,18 @@ export interface ESLintRules extends Linter.RulesRecord { * @default false */ allowNamedExports: boolean; + /** + * @default true + */ + enums: boolean; + /** + * @default true + */ + typedefs: boolean; + /** + * @default true + */ + ignoreTypeReferences: boolean; }> | "nofunc", ]