diff --git a/src/rules/noUnsafeAnyRule.ts b/src/rules/noUnsafeAnyRule.ts index d5cfc992c7a..7e8ea790a34 100644 --- a/src/rules/noUnsafeAnyRule.ts +++ b/src/rules/noUnsafeAnyRule.ts @@ -47,14 +47,13 @@ export class Rule extends Lint.Rules.TypedRule { const isExpression: (node: ts.Node) => node is ts.Expression = (ts as any).isExpression; function walk(ctx: Lint.WalkContext, checker: ts.TypeChecker): void { - return ts.forEachChild(ctx.sourceFile, recur); - function recur(node: ts.Node): void { + return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void { if (isExpression(node) && isAny(checker.getTypeAtLocation(node)) && !isAllowedLocation(node, checker)) { ctx.addFailureAtNode(node, Rule.FAILURE_STRING); } else { - return ts.forEachChild(node, recur); + return ts.forEachChild(node, cb); } - } + }); } function isAllowedLocation(node: ts.Expression, { getContextualType, getTypeAtLocation }: ts.TypeChecker): boolean { @@ -67,6 +66,13 @@ function isAllowedLocation(node: ts.Expression, { getContextualType, getTypeAtLo // Allow casts case ts.SyntaxKind.TypeAssertionExpression: case ts.SyntaxKind.AsExpression: + // Allow imports + case ts.SyntaxKind.ImportEqualsDeclaration: + case ts.SyntaxKind.ExternalModuleReference: + case ts.SyntaxKind.ImportDeclaration: + case ts.SyntaxKind.NamespaceImport: + case ts.SyntaxKind.ImportSpecifier: + case ts.SyntaxKind.ImportClause: return true; // OK to pass 'any' to a function that takes 'any' as its argument @@ -91,10 +97,12 @@ function isAllowedLocation(node: ts.Expression, { getContextualType, getTypeAtLo return false; } - // Allow `const x = foo;`, but not `const x: Foo = foo;`. - case ts.SyntaxKind.VariableDeclaration: - return Lint.hasModifier(parent.parent!.parent!.modifiers, ts.SyntaxKind.DeclareKeyword) || - (parent as ts.VariableDeclaration).type === undefined; + // Allow `const x = foo;` and `const x: any = foo`, but not `const x: Foo = foo;`. + case ts.SyntaxKind.VariableDeclaration: { + const { name, type } = parent as ts.VariableDeclaration; + // Always allow the LHS to be `any`. Just don't allow RHS to be `any` when LHS isn't. + return node === name || type === undefined || type.kind === ts.SyntaxKind.AnyKeyword; + } case ts.SyntaxKind.PropertyAccessExpression: // Don't warn for right hand side; this is redundant if we warn for the left-hand side. diff --git a/test/rules/no-unsafe-any/commonjsModule.ts b/test/rules/no-unsafe-any/commonjsModule.ts new file mode 100644 index 00000000000..d38fad21b9c --- /dev/null +++ b/test/rules/no-unsafe-any/commonjsModule.ts @@ -0,0 +1,3 @@ +const x: any = 0; +namespace x {} +export = x; diff --git a/test/rules/no-unsafe-any/es6Module.ts b/test/rules/no-unsafe-any/es6Module.ts new file mode 100644 index 00000000000..592305a9d7e --- /dev/null +++ b/test/rules/no-unsafe-any/es6Module.ts @@ -0,0 +1,3 @@ +const defaultExport: any = 0; +export default defaultExport; +export const namedExport: any = 0; diff --git a/test/rules/no-unsafe-any/test.ts.lint b/test/rules/no-unsafe-any/test.ts.lint index 7222f2ddb10..142a3d8dcf4 100644 --- a/test/rules/no-unsafe-any/test.ts.lint +++ b/test/rules/no-unsafe-any/test.ts.lint @@ -1,3 +1,10 @@ +import importEquals = require("./commonjsModule"); +import importAlias = modA; +namespace N { const x: any = 0; } +import importQualifiedName = N.x; +import * as namespaceImport from "./commonjsModule"; +import defaultExport, { namedExport } from "./es6Module"; + declare const x: any; function f(x: any) {