📄 no-use-before-define¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 16 |
| 📦 Imports | 6 |
| 📊 Variables & Constants | 1 |
| 📐 Interfaces | 1 |
| 📑 Type Aliases | 2 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/no-use-before-define.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'no-use-before-define' |
meta.type |
'problem' |
meta.docs.description |
'Disallow the use of variables before they are defined' |
meta.docs.extendsBaseRule |
true |
meta.messages.noUseBeforeDefine |
"'{{name}}' was used before it was defined." |
meta.schema |
[ { oneOf: [ { type: 'string', description: 'Broadly set functions and allowNamedExports to false.', enum: ['nofunc']... |
defaultOptions |
[ { allowNamedExports: false, classes: true, enums: true, functions: true, ignoreTypeReferences: true, typedefs: true... |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESTree |
@typescript-eslint/utils |
DefinitionType |
@typescript-eslint/scope-manager |
AST_NODE_TYPES |
@typescript-eslint/utils |
TSESLint |
@typescript-eslint/utils |
createRule |
../util |
referenceContainsTypeQuery |
../util/referenceContainsTypeQuery |
Variables & Constants¶
| Name | Type | Kind | Value | Exported |
|---|---|---|---|---|
SENTINEL_TYPE |
RegExp |
const | /^(?:(?:Function\|Class)(?:Declaration\|Expression)\|ArrowFunctionExpression\... |
✗ |
Functions¶
create(context: any, optionsWithDefault: any): { Program(node: any): void; }¶
Parameters:
contextanyoptionsWithDefaultany
Returns: { Program(node: any): void; }
Calls:
parseOptionsisTypeReferenceisFunctionisOuterClassisOuterVariableisOuterEnumisTypedefisInInitializerscope.references.forEachcontext.reportisNamedExportsisDefinedBeforeUsereportisForbiddenisClassRefInClassDecoratorscope.childScopes.forEachfindVariablesInScopecontext.sourceCode.getScope
Internal Comments:
/**
* Determines whether a given use-before-define case should be reported according to the options.
* @param variable The variable that gets used before being defined
* @param reference The reference to the variable
*/
/**
* Finds and validates all variables in a given scope.
*/
// Skips when the reference is:
// - initializations.
// - referring to an undefined variable.
// - referring to a global environment variable (there're no identifiers).
// - located preceded by the variable (except in initializers).
// - allowed by options.
// Reports. (x3)
Code
create(context, optionsWithDefault) {
const options = parseOptions(optionsWithDefault[0]);
/**
* Determines whether a given use-before-define case should be reported according to the options.
* @param variable The variable that gets used before being defined
* @param reference The reference to the variable
*/
function isForbidden(
variable: TSESLint.Scope.Variable,
reference: TSESLint.Scope.Reference,
): boolean {
if (options.ignoreTypeReferences && isTypeReference(reference)) {
return false;
}
if (isFunction(variable)) {
return options.functions;
}
if (isOuterClass(variable, reference)) {
return options.classes;
}
if (isOuterVariable(variable, reference)) {
return options.variables;
}
if (isOuterEnum(variable, reference)) {
return options.enums;
}
if (isTypedef(variable)) {
return options.typedefs;
}
return true;
}
function isDefinedBeforeUse(
variable: TSESLint.Scope.Variable,
reference: TSESLint.Scope.Reference,
): boolean {
return (
variable.identifiers[0].range[1] <= reference.identifier.range[1] &&
!(reference.isValueReference && isInInitializer(variable, reference))
);
}
/**
* Finds and validates all variables in a given scope.
*/
function findVariablesInScope(scope: TSESLint.Scope.Scope): void {
scope.references.forEach(reference => {
const variable = reference.resolved;
function report(): void {
context.report({
node: reference.identifier,
messageId: 'noUseBeforeDefine',
data: {
name: reference.identifier.name,
},
});
}
// Skips when the reference is:
// - initializations.
// - referring to an undefined variable.
// - referring to a global environment variable (there're no identifiers).
// - located preceded by the variable (except in initializers).
// - allowed by options.
if (reference.init) {
return;
}
if (!options.allowNamedExports && isNamedExports(reference)) {
if (!variable || !isDefinedBeforeUse(variable, reference)) {
report();
}
return;
}
if (!variable) {
return;
}
if (
variable.identifiers.length === 0 ||
isDefinedBeforeUse(variable, reference) ||
!isForbidden(variable, reference) ||
isClassRefInClassDecorator(variable, reference) ||
reference.from.type === TSESLint.Scope.ScopeType.functionType
) {
return;
}
// Reports.
report();
});
scope.childScopes.forEach(findVariablesInScope);
}
return {
Program(node): void {
findVariablesInScope(context.sourceCode.getScope(node));
},
};
}
parseOptions(options: string | Config | null): Required<Config>¶
Parses a given value as options.
Code
function parseOptions(options: string | Config | null): Required<Config> {
let functions = true;
let classes = true;
let enums = true;
let variables = true;
let typedefs = true;
let ignoreTypeReferences = true;
let allowNamedExports = false;
if (typeof options === 'string') {
functions = options !== 'nofunc';
} else if (typeof options === 'object' && options != null) {
functions = options.functions !== false;
classes = options.classes !== false;
enums = options.enums !== false;
variables = options.variables !== false;
typedefs = options.typedefs !== false;
ignoreTypeReferences = options.ignoreTypeReferences !== false;
allowNamedExports = options.allowNamedExports !== false;
}
return {
allowNamedExports,
classes,
enums,
functions,
ignoreTypeReferences,
typedefs,
variables,
};
}
isFunction(variable: TSESLint.Scope.Variable): boolean¶
Checks whether or not a given variable is a function declaration.
Code
isTypedef(variable: TSESLint.Scope.Variable): boolean¶
Checks whether or not a given variable is a type declaration.
Code
isOuterEnum(variable: TSESLint.Scope.Variable, reference: TSESLint.Scope.Reference): boolean¶
Checks whether or not a given variable is a enum declaration.
Code
isOuterClass(variable: TSESLint.Scope.Variable, reference: TSESLint.Scope.Reference): boolean¶
Checks whether or not a given variable is a class declaration in an upper function scope.
Raw JSDoc
Code
isOuterVariable(variable: TSESLint.Scope.Variable, reference: TSESLint.Scope.Reference): boolean¶
Checks whether or not a given variable is a variable declaration in an upper function scope.
Raw JSDoc
Code
isNamedExports(reference: TSESLint.Scope.Reference): boolean¶
Checks whether or not a given reference is a export reference.
Code
isTypeReference(reference: TSESLint.Scope.Reference): boolean¶
Checks whether or not a given reference is a type reference.
Calls:
referenceContainsTypeQuery (from ../util/referenceContainsTypeQuery)
Code
isInRange(node: TSESTree.Expression | null | undefined, location: number): boolean¶
Checks whether or not a given location is inside of the range of a given node.
Code
isClassRefInClassDecorator(variable: TSESLint.Scope.Variable, reference: TSESLint.Scope.Reference): boolean¶
Decorators are transpiled such that the decorator is placed after the class declaration So it is considered safe
Raw JSDoc
Code
function isClassRefInClassDecorator(
variable: TSESLint.Scope.Variable,
reference: TSESLint.Scope.Reference,
): boolean {
if (
variable.defs[0].type !== DefinitionType.ClassName ||
variable.defs[0].node.decorators.length === 0
) {
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;
}
isInInitializer(variable: TSESLint.Scope.Variable, reference: TSESLint.Scope.Reference): boolean¶
Checks whether or not a given reference is inside of the initializers of a given variable.
Returns: undefined
true in the following cases:
- var a = a
- var [a = a] = list
- var {a = a} = obj
- for (var a in a) {}
- for (var a of a) {}
Raw JSDoc
Calls:
isInRangeSENTINEL_TYPE.test
Code
function isInInitializer(
variable: TSESLint.Scope.Variable,
reference: TSESLint.Scope.Reference,
): boolean {
if (variable.scope !== reference.from) {
return false;
}
let node: TSESTree.Node | undefined = variable.identifiers[0].parent;
const location = reference.identifier.range[1];
while (node) {
if (node.type === AST_NODE_TYPES.VariableDeclarator) {
if (isInRange(node.init, location)) {
return true;
}
if (
(node.parent.parent.type === AST_NODE_TYPES.ForInStatement ||
node.parent.parent.type === AST_NODE_TYPES.ForOfStatement) &&
isInRange(node.parent.parent.right, location)
) {
return true;
}
break;
} else if (node.type === AST_NODE_TYPES.AssignmentPattern) {
if (isInRange(node.right, location)) {
return true;
}
} else if (SENTINEL_TYPE.test(node.type)) {
break;
}
node = node.parent;
}
return false;
}
Internal helpers¶
Declared inside another function in this file.
isForbidden(variable: TSESLint.Scope.Variable, reference: TSESLint.Scope.Reference): boolean¶
Determines whether a given use-before-define case should be reported according to the options.
Parameters:
variableany: The variable that gets used before being definedreferenceany: The reference to the variable
Raw JSDoc
Calls:
isTypeReferenceisFunctionisOuterClassisOuterVariableisOuterEnumisTypedef
Code
function isForbidden(
variable: TSESLint.Scope.Variable,
reference: TSESLint.Scope.Reference,
): boolean {
if (options.ignoreTypeReferences && isTypeReference(reference)) {
return false;
}
if (isFunction(variable)) {
return options.functions;
}
if (isOuterClass(variable, reference)) {
return options.classes;
}
if (isOuterVariable(variable, reference)) {
return options.variables;
}
if (isOuterEnum(variable, reference)) {
return options.enums;
}
if (isTypedef(variable)) {
return options.typedefs;
}
return true;
}
isDefinedBeforeUse(variable: TSESLint.Scope.Variable, reference: TSESLint.Scope.Reference): boolean¶
Parameters:
variableTSESLint.Scope.VariablereferenceTSESLint.Scope.Reference
Returns: boolean
Calls:
isInInitializer
Code
findVariablesInScope(scope: TSESLint.Scope.Scope): void¶
Finds and validates all variables in a given scope.
Calls:
scope.references.forEachcontext.reportisNamedExportsisDefinedBeforeUsereportisForbiddenisClassRefInClassDecoratorscope.childScopes.forEach
Internal Comments:
// Skips when the reference is:
// - initializations.
// - referring to an undefined variable.
// - referring to a global environment variable (there're no identifiers).
// - located preceded by the variable (except in initializers).
// - allowed by options.
// Reports. (x3)
Code
function findVariablesInScope(scope: TSESLint.Scope.Scope): void {
scope.references.forEach(reference => {
const variable = reference.resolved;
function report(): void {
context.report({
node: reference.identifier,
messageId: 'noUseBeforeDefine',
data: {
name: reference.identifier.name,
},
});
}
// Skips when the reference is:
// - initializations.
// - referring to an undefined variable.
// - referring to a global environment variable (there're no identifiers).
// - located preceded by the variable (except in initializers).
// - allowed by options.
if (reference.init) {
return;
}
if (!options.allowNamedExports && isNamedExports(reference)) {
if (!variable || !isDefinedBeforeUse(variable, reference)) {
report();
}
return;
}
if (!variable) {
return;
}
if (
variable.identifiers.length === 0 ||
isDefinedBeforeUse(variable, reference) ||
!isForbidden(variable, reference) ||
isClassRefInClassDecorator(variable, reference) ||
reference.from.type === TSESLint.Scope.ScopeType.functionType
) {
return;
}
// Reports.
report();
});
scope.childScopes.forEach(findVariablesInScope);
}
report(): void¶
Returns: void
Calls:
context.report
Code
Interfaces¶
Config¶
Interface Code
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
allowNamedExports |
boolean |
✓ | not shown |
classes |
boolean |
✓ | not shown |
enums |
boolean |
✓ | not shown |
functions |
boolean |
✓ | not shown |
ignoreTypeReferences |
boolean |
✓ | not shown |
typedefs |
boolean |
✓ | not shown |
variables |
boolean |
✓ | not shown |
Type Aliases¶
Options¶
MessageIds¶
Generated by Syntax Scribe