Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

no-var-keyword: Allow global var declarations #2513

Merged
merged 2 commits into from Apr 12, 2017
Merged

no-var-keyword: Allow global var declarations #2513

merged 2 commits into from Apr 12, 2017

Conversation

ghost
Copy link

@ghost ghost commented Apr 6, 2017

PR checklist

  • Addresses an existing issue: #0000
  • New feature, bugfix, or enhancement
    • Includes tests
  • Documentation update

Overview of change:

The var keyword is useful in one case: When you want to declare a global variable.
The problem with declaring a global const is that this is considered a block-scoped declaration, and will warn for duplicates.

declare global {
    var x: number;
    var x: number; // OK
    const y: number;
    const y: number; // Error
}

This PR changes the rule to allow var in a position where it declares a global variable.
It still warns for:

  • export var x: number; (not global)
  • var x; (not a declaration if not in declare global)

Note: Also disabled fixer in declaration files, because those should almost always be const instead of let, and we can't detect prefer-const in declarations (#2390).

CHANGELOG.md entry:

[enhancement] no-var-keyword: Allow global var declarations


return isNodeFlagSet(parentNode!, ts.NodeFlags.Let)
|| isNodeFlagSet(parentNode!, ts.NodeFlags.Const);
export function isBlockScopedVariableDeclarationList(node: ts.VariableDeclarationList): boolean {// tslint:disable-next-line no-bitwise
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function is already available from tsutils, so there is no need to duplicate it here

function walk(ctx: Lint.WalkContext<void>): void {
const { sourceFile } = ctx;
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
switch (node.kind) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could simplify the condition and replace the whole switch statement with this:

if (isVariableDeclarationList(node) && !isBlockScopedVariableDeclarationList(node) &&
    (node.parent!.kind !== ts.SyntaxKind.VariableStatement || !isGlobalVarDeclaration(node.parent!)))
  fail(node);

you could even consider inlining fail

function fail(node: ts.Node): void {
// Don't apply fix in a declaration file, because may have meant 'const'.
const fix = sourceFile.isDeclarationFile ? undefined : new Lint.Replacement(node.getStart(), "var".length, "let");
ctx.addFailureAtNode(Lint.childOfKind(node, ts.SyntaxKind.VarKeyword)!, Rule.FAILURE_STRING, fix);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need for addFailureAtNode with childOfKind, you already computed the position of the var keyword for the fix one line above.

this.createReplacement(nodeStart, "var".length, "let"));
}
function isDeclareGlobal(node: ts.Node): boolean {
return isModuleDeclaration(node) && node.name.kind === ts.SyntaxKind.Identifier && node.name.text === "global";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ModuleBlock.parent is always a ModuleDeclaration. typescript@next contains the correct types.
for now it is save to just assert the type

super.visitForOfStatement(node);
function fail(node: ts.Node): void {
// Don't apply fix in a declaration file, because may have meant 'const'.
const fix = sourceFile.isDeclarationFile ? undefined : new Lint.Replacement(node.getStart(), "var".length, "let");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider passing sourceFile as argument to node.getStart()

this.createReplacement(nodeStart, "var".length, "let"));
}
function isDeclareGlobal(node: ts.Node): boolean {
return isModuleDeclaration(node) && node.name.kind === ts.SyntaxKind.Identifier && node.name.text === "global";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could even simplify this further:

return (node.flags & ts.NodeFlags.GlobalAugmentation) !== 0;

In addition you avoid false positived for namespace foo.global {} and namespace foo { export namespace global {} } (those are also good candidates for a test)

You can even consider inlining this condition.

@adidahiya adidahiya added this to the TSLint v5.2 milestone Apr 12, 2017
@adidahiya adidahiya merged commit 7816eeb into palantir:master Apr 12, 2017
@ghost ghost deleted the var branch April 12, 2017 14:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants