1import { TSESTree } from '@typescript-eslint/experimental-utils'; 2import * as util from '../util'; 3 4export type Options = [ 5 { 6 ignoreStringArrays?: boolean; 7 }, 8]; 9export type MessageIds = 'requireCompare'; 10 11export default util.createRule<Options, MessageIds>({ 12 name: 'require-array-sort-compare', 13 defaultOptions: [ 14 { 15 ignoreStringArrays: false, 16 }, 17 ], 18 19 meta: { 20 type: 'problem', 21 docs: { 22 description: 23 'Requires `Array#sort` calls to always provide a `compareFunction`', 24 category: 'Best Practices', 25 recommended: false, 26 requiresTypeChecking: true, 27 }, 28 messages: { 29 requireCompare: "Require 'compare' argument.", 30 }, 31 schema: [ 32 { 33 type: 'object', 34 properties: { 35 ignoreStringArrays: { 36 type: 'boolean', 37 }, 38 }, 39 }, 40 ], 41 }, 42 43 create(context, [options]) { 44 const service = util.getParserServices(context); 45 const checker = service.program.getTypeChecker(); 46 47 /** 48 * Check if a given node is an array which all elements are string. 49 * @param node 50 */ 51 function isStringArrayNode(node: TSESTree.LeftHandSideExpression): boolean { 52 const type = checker.getTypeAtLocation( 53 service.esTreeNodeToTSNodeMap.get(node), 54 ); 55 if (checker.isArrayType(type) || checker.isTupleType(type)) { 56 const typeArgs = checker.getTypeArguments(type); 57 return typeArgs.every( 58 arg => util.getTypeName(checker, arg) === 'string', 59 ); 60 } 61 return false; 62 } 63 64 return { 65 "CallExpression[arguments.length=0] > MemberExpression[property.name='sort'][computed=false]"( 66 callee: TSESTree.MemberExpression, 67 ): void { 68 const tsNode = service.esTreeNodeToTSNodeMap.get(callee.object); 69 const calleeObjType = util.getConstrainedTypeAtLocation( 70 checker, 71 tsNode, 72 ); 73 74 if (options.ignoreStringArrays && isStringArrayNode(callee.object)) { 75 return; 76 } 77 78 if (util.isTypeArrayTypeOrUnionOfArrayTypes(calleeObjType, checker)) { 79 context.report({ node: callee.parent!, messageId: 'requireCompare' }); 80 } 81 }, 82 }; 83 }, 84}); 85