From b4327d831e86c97431b45ab83e1804cf5fa0b3c9 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 24 Dec 2019 17:01:04 +1030 Subject: [PATCH] feat: more checks --- packages/eslint-plugin-tslint/jest.config.js | 5 + packages/eslint-plugin/jest.config.js | 5 + .../eslint-plugin/src/rules/no-unsafe-any.ts | 321 +++++++++++++----- .../tests/rules/no-unsafe-any.test.ts | 239 ++++++++++--- packages/experimental-utils/jest.config.js | 5 + packages/parser/jest.config.js | 5 + packages/typescript-estree/jest.config.js | 1 + 7 files changed, 459 insertions(+), 122 deletions(-) diff --git a/packages/eslint-plugin-tslint/jest.config.js b/packages/eslint-plugin-tslint/jest.config.js index 862b0dda600..65b5e681aaf 100644 --- a/packages/eslint-plugin-tslint/jest.config.js +++ b/packages/eslint-plugin-tslint/jest.config.js @@ -10,4 +10,9 @@ module.exports = { collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], coverageReporters: ['text-summary', 'lcov'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, }; diff --git a/packages/eslint-plugin/jest.config.js b/packages/eslint-plugin/jest.config.js index b64d433b01a..4c8b46a1d4a 100644 --- a/packages/eslint-plugin/jest.config.js +++ b/packages/eslint-plugin/jest.config.js @@ -10,4 +10,9 @@ module.exports = { collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], coverageReporters: ['text-summary', 'lcov'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, }; diff --git a/packages/eslint-plugin/src/rules/no-unsafe-any.ts b/packages/eslint-plugin/src/rules/no-unsafe-any.ts index f0f8cfd6dc7..6093b433c3d 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-any.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-any.ts @@ -1,24 +1,31 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { + TSESTree, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; import { isTypeReference } from 'tsutils'; import * as ts from 'typescript'; import * as util from '../util'; type Options = [ { - allowAnnotationFromAny?: boolean; + allowVariableAnnotationFromAny?: boolean; }, ]; type MessageIds = | 'typeReferenceResolvesToAny' | 'variableDeclarationInitialisedToAnyWithoutAnnotation' | 'variableDeclarationInitialisedToAnyWithAnnotation' + | 'patternVariableDeclarationInitialisedToAny' | 'letVariableInitialisedToNullishAndNoAnnotation' | 'letVariableWithNoInitialAndNoAnnotation' - | 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation' | 'loopVariableInitialisedToAny' | 'returnAny' | 'passedArgumentIsAny' - | 'assignmentValueIsAny'; + | 'assignmentValueIsAny' + | 'updateExpressionIsAny' + | 'booleanTestIsAny' + | 'switchDiscriminantIsAny' + | 'switchCaseTestIsAny'; export default util.createRule({ name: 'no-unsafe-any', @@ -36,24 +43,28 @@ export default util.createRule({ variableDeclarationInitialisedToAnyWithAnnotation: 'Variable declaration is initialised to `any` with an explicit type annotation, which is potentially unsafe. Prefer explicit type narrowing via type guards.', variableDeclarationInitialisedToAnyWithoutAnnotation: - 'Variable declaration is initialised to `any` without an assertion or a type annotation.', + 'Variable declaration is initialised to `any` without a type annotation.', + patternVariableDeclarationInitialisedToAny: + 'Variable declaration is initialised to `any`.', letVariableInitialisedToNullishAndNoAnnotation: 'Variable declared with {{kind}} and initialised to `null` or `undefined` is implicitly typed as `any`. Add an explicit type annotation.', letVariableWithNoInitialAndNoAnnotation: 'Variable declared with {{kind}} with no initial value is implicitly typed as `any`.', - variableDeclarationInitialisedToAnyArrayWithoutAnnotation: - 'Variable declaration is initialised to `any[]` without an assertion or a type annotation.', loopVariableInitialisedToAny: 'Loop variable is typed as `any`.', returnAny: 'The type of the return is `any`.', passedArgumentIsAny: 'The passed argument is `any`.', assignmentValueIsAny: 'The value being assigned is `any`.', + updateExpressionIsAny: 'The update expression variable is `any`.', + booleanTestIsAny: 'The {{kind}} test is `any`.', + switchDiscriminantIsAny: 'The switch discriminant is `any`.', + switchCaseTestIsAny: 'The switch case test is `any`.', }, schema: [ { type: 'object', additionalProperties: false, properties: { - allowAnnotationFromAny: { + allowVariableAnnotationFromAny: { type: 'boolean', }, }, @@ -62,10 +73,10 @@ export default util.createRule({ }, defaultOptions: [ { - allowAnnotationFromAny: false, + allowVariableAnnotationFromAny: false, }, ], - create(context, [{ allowAnnotationFromAny }]) { + create(context, [{ allowVariableAnnotationFromAny }]) { const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); const checker = program.getTypeChecker(); const sourceCode = context.getSourceCode(); @@ -89,11 +100,66 @@ export default util.createRule({ ); } + function isAnyOrAnyArrayType(node: ts.Node): boolean { + return isAnyType(node) || isAnyArrayType(node); + } + + function reportVariableDeclarationInitialisedToAny( + node: TSESTree.VariableDeclarator, + ): void { + if (!node.id.typeAnnotation) { + return context.report({ + node, + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + }); + } + + // there is a type annotation + + if (allowVariableAnnotationFromAny) { + // there is an annotation on the type, and the user indicated they are okay with the "unsafe" conversion + return; + } + if ( + node.id.typeAnnotation.typeAnnotation.type === + AST_NODE_TYPES.TSUnknownKeyword + ) { + // annotation with unknown is as safe as can be + return; + } + + return context.report({ + node, + messageId: 'variableDeclarationInitialisedToAnyWithAnnotation', + }); + } + + function checkDestructuringPattern(node: TSESTree.Node): void { + if (node.type === AST_NODE_TYPES.ObjectPattern) { + node.properties.forEach(prop => { + checkDestructuringPattern(prop.value ?? prop); + }); + } else if (node.type === AST_NODE_TYPES.ArrayPattern) { + node.elements.forEach(el => { + checkDestructuringPattern(el); + }); + } else { + const tsNode = esTreeNodeToTSNodeMap.get(node); + if (isAnyOrAnyArrayType(tsNode)) { + context.report({ + node, + messageId: 'patternVariableDeclarationInitialisedToAny', + }); + } + } + } + return { // Handled by the no-explicit-any rule (with a fixer) //TSAnyKeyword(node): void {}, - // typeReferenceResolvesToAny + // #region typeReferenceResolvesToAny + TSTypeReference(node): void { const tsNode = esTreeNodeToTSNodeMap.get(node); if (!isAnyType(tsNode)) { @@ -110,7 +176,10 @@ export default util.createRule({ }); }, - // letVariableWithNoInitialAndNoAnnotation + // #endregion typeReferenceResolvesToAny + + // #region letVariableWithNoInitialAndNoAnnotation + 'VariableDeclaration:matches([kind = "let"], [kind = "var"]) > VariableDeclarator:not([init])'( node: TSESTree.VariableDeclarator, ): void { @@ -128,7 +197,10 @@ export default util.createRule({ }); }, - // letVariableInitialisedToNullishAndNoAnnotation + // #endregion letVariableWithNoInitialAndNoAnnotation + + // #region letVariableInitialisedToNullishAndNoAnnotation + 'VariableDeclaration:matches([kind = "let"], [kind = "var"]) > VariableDeclarator[init]'( node: TSESTree.VariableDeclarator, ): void { @@ -151,102 +223,91 @@ export default util.createRule({ } }, - // variableDeclarationInitialisedToAnyWithAnnotation - // variableDeclarationInitialisedToAnyWithoutAnnotation - 'VariableDeclaration > VariableDeclarator[init]'( - node: TSESTree.VariableDeclarator, + // #endregion letVariableInitialisedToNullishAndNoAnnotation + + // #region variableDeclarationInitialisedToAnyWithAnnotation, variableDeclarationInitialisedToAnyWithoutAnnotation, patternVariableDeclarationInitialisedToAny + + // const x = ...; + 'VariableDeclaration > VariableDeclarator[init] > Identifier.id'( + node: TSESTree.Identifier, ): void { - if (!node.init || util.isTypeAssertion(node.init)) { - // type assertions are handled via their own selector + const parent = node.parent as TSESTree.VariableDeclarator; + /* istanbul ignore if */ if (!parent.init) { return; } - const tsNode = esTreeNodeToTSNodeMap.get(node.init); - if (!isAnyType(tsNode)) { + const tsNode = esTreeNodeToTSNodeMap.get(parent.init); + if (!isAnyType(tsNode) && !isAnyArrayType(tsNode)) { return; } - // the variable is initialised to any... - - if (!node.id.typeAnnotation) { - return context.report({ - node, - messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', - }); - } - - // there is a type annotation + // the variable is initialised to any | any[]... - if (allowAnnotationFromAny) { - // there is an annotation on the type, and the user indicated they are okay with the "unsafe" conversion - return; - } - - return context.report({ - node, - messageId: 'variableDeclarationInitialisedToAnyWithAnnotation', - }); + reportVariableDeclarationInitialisedToAny(parent); }, - - // #region variableDeclarationInitialisedToAnyArrayWithoutAnnotation - - // const x = [] + // const x = []; + // this is a special case, because the type of [] is never[], but the variable gets typed as any[]. + // this means it can't be caught by the above selector 'VariableDeclaration > VariableDeclarator > ArrayExpression[elements.length = 0].init'( node: TSESTree.ArrayExpression, ): void { const parent = node.parent as TSESTree.VariableDeclarator; + if (parent.id.typeAnnotation) { + // note that there is no way to fix the type, so you have to use a type annotation + // so we don't report variableDeclarationInitialisedToAnyWithAnnotation return; } context.report({ node: parent, - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', }); }, - [[ - // const x = Array(...) - 'VariableDeclaration > VariableDeclarator > CallExpression[callee.name = "Array"].init', - // const x = new Array(...) - 'VariableDeclaration > VariableDeclarator > NewExpression[callee.name = "Array"].init', - ].join(', ')]( - node: TSESTree.CallExpression | TSESTree.NewExpression, + // const { x } = ...; + 'VariableDeclaration > VariableDeclarator[init] > ObjectPattern.id'( + node: TSESTree.ObjectPattern, ): void { const parent = node.parent as TSESTree.VariableDeclarator; - if (parent.id.typeAnnotation) { + /* istanbul ignore if */ if (!parent.init) { return; } - if (node.arguments.length > 1) { - // Array(1, 2) === [1, 2] + const tsNode = esTreeNodeToTSNodeMap.get(parent.init); + if (isAnyOrAnyArrayType(tsNode)) { + // the entire init value is any, so report the entire declaration + return reportVariableDeclarationInitialisedToAny(parent); + } + + checkDestructuringPattern(node); + }, + // const [x] = ...; + 'VariableDeclaration > VariableDeclarator[init] > ArrayPattern.id'( + node: TSESTree.ArrayPattern, + ): void { + const parent = node.parent as TSESTree.VariableDeclarator; + /* istanbul ignore if */ if (!parent.init) { return; } - if (node.arguments.length === 1) { - // check if the 1 argument is a number, as Array(1) === [empty] === any[] - const tsNode = esTreeNodeToTSNodeMap.get(node.arguments[0]); - const type = checker.getTypeAtLocation(tsNode); - if (!util.isTypeFlagSetNonUnion(type, ts.TypeFlags.NumberLike)) { - return; - } + const tsNode = esTreeNodeToTSNodeMap.get(parent.init); + if (isAnyOrAnyArrayType(tsNode)) { + // the entire init value is any, so report the entire declaration + return reportVariableDeclarationInitialisedToAny(parent); } - context.report({ - node: parent, - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', - }); + checkDestructuringPattern(node); }, - // #endregion variableDeclarationInitialisedToAnyArrayWithoutAnnotation + // #endregion variableDeclarationInitialisedToAnyWithAnnotation, variableDeclarationInitialisedToAnyWithoutAnnotation, patternVariableDeclarationInitialisedToAny + + // #region loopVariableInitialisedToAny - // loopVariableInitialisedToAny 'ForOfStatement > VariableDeclaration.left > VariableDeclarator'( node: TSESTree.VariableDeclarator, ): void { const tsNode = esTreeNodeToTSNodeMap.get(node); - if (isAnyType(tsNode) || isAnyArrayType(tsNode)) { + if (isAnyOrAnyArrayType(tsNode)) { return context.report({ node, messageId: 'loopVariableInitialisedToAny', @@ -254,7 +315,10 @@ export default util.createRule({ } }, - // returnAny + // #endregion loopVariableInitialisedToAny + + // #region returnAny + 'ReturnStatement[argument]'(node: TSESTree.ReturnStatement): void { const argument = util.nullThrows( node.argument, @@ -262,7 +326,20 @@ export default util.createRule({ ); const tsNode = esTreeNodeToTSNodeMap.get(argument); - if (isAnyType(tsNode) || isAnyArrayType(tsNode)) { + if (isAnyOrAnyArrayType(tsNode)) { + context.report({ + node, + messageId: 'returnAny', + }); + } + }, + // () => 1 + 'ArrowFunctionExpression > :not(TSESTree.BlockStatement).body'( + node: TSESTree.Expression, + ): void { + const tsNode = esTreeNodeToTSNodeMap.get(node); + + if (isAnyOrAnyArrayType(tsNode)) { context.report({ node, messageId: 'returnAny', @@ -270,33 +347,121 @@ export default util.createRule({ } }, - // passedArgumentIsAny + // #endregion returnAny + + // #region passedArgumentIsAny + 'CallExpression[arguments.length > 0]'( node: TSESTree.CallExpression, ): void { for (const argument of node.arguments) { const tsNode = esTreeNodeToTSNodeMap.get(argument); - if (isAnyType(tsNode) || isAnyArrayType(tsNode)) { + if (isAnyOrAnyArrayType(tsNode)) { context.report({ - node, + node: argument, messageId: 'passedArgumentIsAny', }); } } }, - // assignmentValueIsAny + // #endregion passedArgumentIsAny + + // #region assignmentValueIsAny + AssignmentExpression(node): void { const tsNode = esTreeNodeToTSNodeMap.get(node.right); - if (isAnyType(tsNode) || isAnyArrayType(tsNode)) { + if (isAnyOrAnyArrayType(tsNode)) { context.report({ node, messageId: 'assignmentValueIsAny', }); } }, + + // #endregion assignmentValueIsAny + + // #region updateExpressionIsAny + + UpdateExpression(node): void { + const tsNode = esTreeNodeToTSNodeMap.get(node.argument); + + if (isAnyType(tsNode)) { + context.report({ + node, + messageId: 'updateExpressionIsAny', + }); + } + }, + + // #endregion updateExpressionIsAny + + // #region booleanTestIsAny + + 'IfStatement, WhileStatement, DoWhileStatement, ConditionalExpression'( + node: + | TSESTree.IfStatement + | TSESTree.WhileStatement + | TSESTree.DoWhileStatement + | TSESTree.ConditionalExpression, + ): void { + const tsNode = esTreeNodeToTSNodeMap.get(node.test); + const typeToText = { + [AST_NODE_TYPES.IfStatement]: 'if', + [AST_NODE_TYPES.WhileStatement]: 'while', + [AST_NODE_TYPES.DoWhileStatement]: 'do while', + [AST_NODE_TYPES.ConditionalExpression]: 'ternary', + }; + + if (isAnyOrAnyArrayType(tsNode)) { + context.report({ + node: node.test, + messageId: 'booleanTestIsAny', + data: { + kind: typeToText[node.type], + }, + }); + } + }, + + // #endregion booleanTestIsAny + + // #region switchDiscriminantIsAny + + SwitchStatement(node): void { + const tsNode = esTreeNodeToTSNodeMap.get(node.discriminant); + + if (isAnyOrAnyArrayType(tsNode)) { + context.report({ + node: node.discriminant, + messageId: 'switchDiscriminantIsAny', + }); + } + }, + + // #endregion switchDiscriminantIsAny + + // #region switchCaseTestIsAny + + 'SwitchCase[test]'(node: TSESTree.SwitchCase): void { + const tsNode = esTreeNodeToTSNodeMap.get( + util.nullThrows( + node.test, + util.NullThrowsReasons.MissingToken('test', 'SwitchCase'), + ), + ); + + if (isAnyOrAnyArrayType(tsNode)) { + context.report({ + node, + messageId: 'switchCaseTestIsAny', + }); + } + }, + + // #endregion switchCaseTestIsAny }; }, }); diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-any.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-any.test.ts index e17f1cd5357..2591f99bfc3 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-any.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-any.test.ts @@ -24,7 +24,7 @@ ruleTester.run('no-unsafe-any', rule, { const x: any = 1; const y: number = x; `, - options: [{ allowAnnotationFromAny: true }], + options: [{ allowVariableAnnotationFromAny: true }], }, 'const a = Array("str");', 'const a = ["str"];', @@ -37,6 +37,13 @@ ruleTester.run('no-unsafe-any', rule, { 'x = 1', 'function foo(arg: string) { return arg; }', 'function foo() { return 1; }', + '1++;', + 'if (1) {}', + 'while (1) {}', + 'do {} while (1)', + '(1) ? 1 : 2;', + 'switch (1) { case 1: }', + 'switch (1) { default: }', ], invalid: [ // typeReferenceResolvesToAny @@ -162,97 +169,134 @@ let a: string | null = null, b = null; }), // variableDeclarationInitialisedToAnyWithoutAnnotation - { + ...batchedSingleLineTests({ code: ` -const a: any = 1; +const b = (1 as any); +const c = (1 as any) + 1; +const { baz } = (1 as any); -const b = a; -const c = a + 1; +const a = []; +const b = Array(); +const c = new Array(); +const d = Array(1); +const e = new Array(1); `, - options: [{ allowAnnotationFromAny: false }], errors: [ + { + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + line: 2, + }, + { + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + line: 3, + }, { messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', line: 4, }, { messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', - line: 5, + line: 6, + }, + { + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + line: 7, + }, + { + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + line: 8, + }, + { + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + line: 9, + }, + { + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + line: 10, + }, + ], + }), + { + code: ` + const a = 1; + const b = new Array(a); + `, + errors: [ + { + messageId: 'variableDeclarationInitialisedToAnyWithoutAnnotation', + line: 3, }, ], }, // variableDeclarationInitialisedToAnyWithAnnotation - { + ...batchedSingleLineTests({ code: ` -const a: any = 1; - -const b: number = a; -const c: number = a + 1; +const bbbb: number = (1 as any); +const c: number = (1 as any) + 1; `, - options: [{ allowAnnotationFromAny: false }], + options: [{ allowVariableAnnotationFromAny: false }], errors: [ { messageId: 'variableDeclarationInitialisedToAnyWithAnnotation', - line: 4, + line: 2, }, { messageId: 'variableDeclarationInitialisedToAnyWithAnnotation', - line: 5, + line: 3, }, ], - }, + }), - // variableDeclarationInitialisedToAnyArrayWithoutAnnotation + // patternVariableDeclarationInitialisedToAny ...batchedSingleLineTests({ code: ` -const a = []; -const b = Array(); -const c = new Array(); -const d = Array(1); -const e = new Array(1); +const { x, y } = { x: 1 as any, y: 1 as any }; +const { x, y } = { x: 1 } as { x: number, y: any }; +const { x: { y } } = { x: { y: 1 as any } }; +const { x: { y: [a, b] } } = { x: { y: [1] } } as { x: { y: any[] } }; +const [a,b] = [1 as any, 2 as any]; +const [{ a }] = [1 as any]; `, errors: [ { - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', + messageId: 'patternVariableDeclarationInitialisedToAny', + line: 2, + }, + { + messageId: 'patternVariableDeclarationInitialisedToAny', line: 2, }, { - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', + messageId: 'patternVariableDeclarationInitialisedToAny', line: 3, }, { - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', + messageId: 'patternVariableDeclarationInitialisedToAny', line: 4, }, { - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', + messageId: 'patternVariableDeclarationInitialisedToAny', line: 5, }, { - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', + messageId: 'patternVariableDeclarationInitialisedToAny', + line: 5, + }, + { + messageId: 'patternVariableDeclarationInitialisedToAny', line: 6, }, - ], - }), - { - code: ` - const a = 1; - const b = new Array(a); - `, - errors: [ { - messageId: - 'variableDeclarationInitialisedToAnyArrayWithoutAnnotation', - line: 3, + messageId: 'patternVariableDeclarationInitialisedToAny', + line: 6, + }, + { + messageId: 'patternVariableDeclarationInitialisedToAny', + line: 7, }, ], - }, + }), // loopVariableInitialisedToAny ...batchedSingleLineTests({ @@ -277,6 +321,9 @@ for (const x of ([] as any[])) {} code: ` function foo(arg: any) { return arg; } function foo(arg: any[]) { return arg; } +function foo() { return (1 as any); } +const foo = () => (1 as any); +const foo = (arg: any[]) => arg; `, errors: [ { @@ -287,6 +334,18 @@ function foo(arg: any[]) { return arg; } messageId: 'returnAny', line: 3, }, + { + messageId: 'returnAny', + line: 4, + }, + { + messageId: 'returnAny', + line: 5, + }, + { + messageId: 'returnAny', + line: 6, + }, ], }), @@ -359,5 +418,97 @@ x.y = (1 as any); }, ], }), + + // updateExpressionIsAny + ...batchedSingleLineTests({ + code: ` +(1 as any)++; +(1 as any)--; +++(1 as any); +--(1 as any); + `, + errors: [ + { + messageId: 'updateExpressionIsAny', + line: 2, + }, + { + messageId: 'updateExpressionIsAny', + line: 3, + }, + { + messageId: 'updateExpressionIsAny', + line: 4, + }, + { + messageId: 'updateExpressionIsAny', + line: 5, + }, + ], + }), + + // booleanTestIsAny + ...batchedSingleLineTests({ + code: ` +if (1 as any) {} +while (1 as any) {} +do {} while (1 as any) +;(1 as any) ? 1 : 2; + `, + errors: [ + { + messageId: 'booleanTestIsAny', + line: 2, + }, + { + messageId: 'booleanTestIsAny', + line: 3, + }, + { + messageId: 'booleanTestIsAny', + line: 4, + }, + { + messageId: 'booleanTestIsAny', + line: 5, + }, + ], + }), + + // switchDiscriminantIsAny + ...batchedSingleLineTests({ + code: ` +switch (1 as any) {} +switch (1 as any[]) {} + `, + errors: [ + { + messageId: 'switchDiscriminantIsAny', + line: 2, + }, + { + messageId: 'switchDiscriminantIsAny', + line: 3, + }, + ], + }), + + // switchCaseTestIsAny + ...batchedSingleLineTests({ + code: ` +switch (1) { case (1 as any): } +switch (1) { case (1 as any[]): } + `, + errors: [ + { + messageId: 'switchCaseTestIsAny', + line: 2, + }, + { + messageId: 'switchCaseTestIsAny', + line: 3, + }, + ], + }), ], }); diff --git a/packages/experimental-utils/jest.config.js b/packages/experimental-utils/jest.config.js index b64d433b01a..4c8b46a1d4a 100644 --- a/packages/experimental-utils/jest.config.js +++ b/packages/experimental-utils/jest.config.js @@ -10,4 +10,9 @@ module.exports = { collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], coverageReporters: ['text-summary', 'lcov'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, }; diff --git a/packages/parser/jest.config.js b/packages/parser/jest.config.js index a74f80d7a06..2be0a4cd2ce 100644 --- a/packages/parser/jest.config.js +++ b/packages/parser/jest.config.js @@ -10,4 +10,9 @@ module.exports = { collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], coverageReporters: ['text-summary', 'lcov'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, }; diff --git a/packages/typescript-estree/jest.config.js b/packages/typescript-estree/jest.config.js index e01f6ed0775..1f8266533cf 100644 --- a/packages/typescript-estree/jest.config.js +++ b/packages/typescript-estree/jest.config.js @@ -12,6 +12,7 @@ module.exports = { coverageReporters: ['text-summary', 'lcov'], globals: { 'ts-jest': { + isolatedModules: true, diagnostics: { // ignore the diagnostic error for the invalidFileErrors fixtures ignoreCodes: [5056],