From 0abe65b440b0043b1ee590bef4f112d7f5775c7b Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Sat, 14 Mar 2020 10:20:14 +1300 Subject: [PATCH 1/3] chore(eslint-plugin): create `isTypeArrayTypeOrUnionOfArrayTypes` util --- packages/eslint-plugin/src/util/types.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/eslint-plugin/src/util/types.ts b/packages/eslint-plugin/src/util/types.ts index ae6ee3843aa8..261c154361b1 100644 --- a/packages/eslint-plugin/src/util/types.ts +++ b/packages/eslint-plugin/src/util/types.ts @@ -13,6 +13,23 @@ import { } from 'tsutils'; import * as ts from 'typescript'; +/** + * Checks if the given type is either an array type, + * or a union made up solely of array types. + */ +export function isTypeArrayTypeOrUnionOfArrayTypes( + type: ts.Type, + checker: ts.TypeChecker, +): boolean { + for (const t of unionTypeParts(type)) { + if (!checker.isArrayType(t)) { + return false; + } + } + + return true; +} + /** * @param type Type being checked by name. * @param allowedNames Symbol names checking on the type. From b616765c0d2733003348ee7aa2dcddcdd4e7c8d8 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Thu, 12 Mar 2020 09:02:14 +1300 Subject: [PATCH 2/3] refactor(eslint-plugin): use `isTypeArrayTypeOrUnionOfArrayTypes` util --- .../src/rules/no-for-in-array.ts | 8 ++-- .../eslint-plugin/src/rules/no-unsafe-call.ts | 3 +- .../src/rules/no-unsafe-return.ts | 3 +- .../src/rules/require-array-sort-compare.ts | 27 ++++-------- .../src/rules/restrict-plus-operands.ts | 12 +----- .../rules/restrict-template-expressions.ts | 11 +---- .../tests/rules/no-for-in-array.test.ts | 42 +++++++++++++++++++ .../tests/rules/no-unsafe-call.test.ts | 7 ++++ .../tests/rules/no-unsafe-return.test.ts | 14 +++++++ 9 files changed, 82 insertions(+), 45 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-for-in-array.ts b/packages/eslint-plugin/src/rules/no-for-in-array.ts index 511b744ee4c0..f89d2ffd08d7 100644 --- a/packages/eslint-plugin/src/rules/no-for-in-array.ts +++ b/packages/eslint-plugin/src/rules/no-for-in-array.ts @@ -25,11 +25,13 @@ export default util.createRule({ const checker = parserServices.program.getTypeChecker(); const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); - const type = checker.getTypeAtLocation(originalNode.expression); + const type = util.getConstrainedTypeAtLocation( + checker, + originalNode.expression, + ); if ( - (typeof type.symbol !== 'undefined' && - type.symbol.name === 'Array') || + util.isTypeArrayTypeOrUnionOfArrayTypes(type, checker) || (type.flags & ts.TypeFlags.StringLike) !== 0 ) { context.report({ diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 66f66448c28b..3661bd44f16d 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -31,7 +31,8 @@ export default util.createRule<[], MessageIds>({ messageId: MessageIds, ): void { const tsNode = esTreeNodeToTSNodeMap.get(node); - const type = checker.getTypeAtLocation(tsNode); + const type = util.getConstrainedTypeAtLocation(checker, tsNode); + if (util.isTypeAnyType(type)) { context.report({ node: reportingNode, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-return.ts b/packages/eslint-plugin/src/rules/no-unsafe-return.ts index fef04cd6365f..5fbbfac9b908 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-return.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-return.ts @@ -74,7 +74,8 @@ export default util.createRule({ } // function has an explicit return type, so ensure it's a safe return - const returnNodeType = checker.getTypeAtLocation( + const returnNodeType = util.getConstrainedTypeAtLocation( + checker, esTreeNodeToTSNodeMap.get(returnNode), ); const functionTSNode = esTreeNodeToTSNodeMap.get(functionNode); diff --git a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts index 25c4c2e57475..2f388d364dd7 100644 --- a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts +++ b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts @@ -1,5 +1,4 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; -import * as ts from 'typescript'; import * as util from '../util'; export default util.createRule({ @@ -27,26 +26,16 @@ export default util.createRule({ return { ":matches(CallExpression, OptionalCallExpression)[arguments.length=0] > :matches(MemberExpression, OptionalMemberExpression)[property.name='sort'][computed=false]"( - node: TSESTree.MemberExpression | TSESTree.OptionalMemberExpression, + callee: TSESTree.MemberExpression | TSESTree.OptionalMemberExpression, ): void { - // Get the symbol of the `sort` method. - const tsNode = service.esTreeNodeToTSNodeMap.get(node); - const sortSymbol = checker.getSymbolAtLocation(tsNode); - if (sortSymbol == null) { - return; - } + const tsNode = service.esTreeNodeToTSNodeMap.get(callee.object); + const calleeObjType = util.getConstrainedTypeAtLocation( + checker, + tsNode, + ); - // Check the owner type of the `sort` method. - for (const methodDecl of sortSymbol.declarations) { - const typeDecl = methodDecl.parent; - if ( - ts.isInterfaceDeclaration(typeDecl) && - ts.isSourceFile(typeDecl.parent) && - typeDecl.name.escapedText === 'Array' - ) { - context.report({ node: node.parent!, messageId: 'requireCompare' }); - return; - } + if (util.isTypeArrayTypeOrUnionOfArrayTypes(calleeObjType, checker)) { + context.report({ node: callee.parent!, messageId: 'requireCompare' }); } }, }; diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index a833825a207c..2702009046ff 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -54,16 +54,6 @@ export default util.createRule({ * Helper function to get base type of node */ function getBaseTypeOfLiteralType(type: ts.Type): BaseLiteral { - const constraint = type.getConstraint(); - if ( - constraint && - // for generic types with union constraints, it will return itself from getConstraint - // so we have to guard against infinite recursion... - constraint !== type - ) { - return getBaseTypeOfLiteralType(constraint); - } - if (type.isNumberLiteral()) { return 'number'; } @@ -98,7 +88,7 @@ export default util.createRule({ */ function getNodeType(node: TSESTree.Expression): BaseLiteral { const tsNode = service.esTreeNodeToTSNodeMap.get(node); - const type = typeChecker.getTypeAtLocation(tsNode); + const type = util.getConstrainedTypeAtLocation(typeChecker, tsNode); return getBaseTypeOfLiteralType(type); } diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 63b257ffe7f7..d73d8d98a254 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -98,21 +98,12 @@ export default util.createRule({ */ function getNodeType(node: TSESTree.Expression): BaseType[] { const tsNode = service.esTreeNodeToTSNodeMap.get(node); - const type = typeChecker.getTypeAtLocation(tsNode); + const type = util.getConstrainedTypeAtLocation(typeChecker, tsNode); return getBaseType(type); } function getBaseType(type: ts.Type): BaseType[] { - const constraint = type.getConstraint(); - if ( - constraint && - // for generic types with union constraints, it will return itself - constraint !== type - ) { - return getBaseType(constraint); - } - if (type.isStringLiteral()) { return ['string']; } diff --git a/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts index 134fd513e877..b9362906ed29 100644 --- a/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts +++ b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts @@ -54,5 +54,47 @@ for (const x in z) { }, ], }, + { + code: ` +const fn = (arr: number[]) => { + for (const x in arr) { + console.log(x); + } +}`, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement, + }, + ], + }, + { + code: ` +const fn = (arr: number[] | string[]) => { + for (const x in arr) { + console.log(x); + } +}`, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement, + }, + ], + }, + { + code: ` +const fn = (arr: T) => { + for (const x in arr) { + console.log(x); + } +}`, + errors: [ + { + messageId: 'forInViolation', + type: AST_NODE_TYPES.ForInStatement, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts index 8adb44b9960f..f860410da5c1 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts @@ -47,6 +47,7 @@ function foo(x: any) { x() } function foo(x: any) { x?.() } function foo(x: any) { x.a.b.c.d.e.f.g() } function foo(x: any) { x.a.b.c.d.e.f.g?.() } +function foo(x: T) { x() } `, errors: [ { @@ -73,6 +74,12 @@ function foo(x: any) { x.a.b.c.d.e.f.g?.() } column: 24, endColumn: 39, }, + { + messageId: 'unsafeCall', + line: 6, + column: 37, + endColumn: 38, + }, ], }), ...batchedSingleLineTests({ diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts index b0c1ead90076..ecfea22d17ad 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts @@ -73,6 +73,20 @@ function foo(): Set { `, ], invalid: [ + { + code: 'function fn(x: T) { return x };', + errors: [ + { + messageId: 'unsafeReturnAssignment', + data: { + sender: 'any', + receiver: 'T', + }, + line: 1, + column: 36, + }, + ], + }, ...batchedSingleLineTests({ code: noFormat` function foo() { return (1 as any); } From b9548f5d005d09670a5ea6b69d1bdf314144729b Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 13 Apr 2020 11:35:28 +1200 Subject: [PATCH 3/3] chore(eslint-plugin): apply formatting --- .../eslint-plugin/tests/rules/no-for-in-array.test.ts | 9 ++++++--- .../eslint-plugin/tests/rules/no-unsafe-return.test.ts | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts index b9362906ed29..7c3ec3091859 100644 --- a/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts +++ b/packages/eslint-plugin/tests/rules/no-for-in-array.test.ts @@ -60,7 +60,8 @@ const fn = (arr: number[]) => { for (const x in arr) { console.log(x); } -}`, +}; + `, errors: [ { messageId: 'forInViolation', @@ -74,7 +75,8 @@ const fn = (arr: number[] | string[]) => { for (const x in arr) { console.log(x); } -}`, +}; + `, errors: [ { messageId: 'forInViolation', @@ -88,7 +90,8 @@ const fn = (arr: T) => { for (const x in arr) { console.log(x); } -}`, +}; + `, errors: [ { messageId: 'forInViolation', diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts index ecfea22d17ad..42a58734a476 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts @@ -74,7 +74,11 @@ function foo(): Set { ], invalid: [ { - code: 'function fn(x: T) { return x };', + code: ` +function fn(x: T) { + return x; +} + `, errors: [ { messageId: 'unsafeReturnAssignment', @@ -82,8 +86,8 @@ function foo(): Set { sender: 'any', receiver: 'T', }, - line: 1, - column: 36, + line: 3, + column: 3, }, ], }, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy