namespace ts . refactor . convertParamsToDestructuredObject { const refactorName = "Convert parameters to destructured object" ; const minimumParameterLength = 2 ; const refactorDescription = getLocaleSpecificMessage ( Diagnostics . Convert_parameters_to_destructured_object ) ; const toDestructuredAction = { name : refactorName , description : refactorDescription , kind : "refactor.rewrite.parameters.toDestructured" } ; registerRefactor ( refactorName , { kinds : [ toDestructuredAction . kind ] , getEditsForAction : getRefactorEditsToConvertParametersToDestructuredObject , getAvailableActions : getRefactorActionsToConvertParametersToDestructuredObject } ) ; function getRefactorActionsToConvertParametersToDestructuredObject ( context ) { const { file , startPosition } = context ; const isJSFile = isSourceFileJS ( file ) ; if ( isJSFile ) return emptyArray ; const functionDeclaration = getFunctionDeclarationAtPosition ( file , startPosition , context . program . getTypeChecker ( ) ) ; if ( ! functionDeclaration ) return emptyArray ; return [ { name : refactorName , description : refactorDescription , actions : [ toDestructuredAction ] } ] ; } function getRefactorEditsToConvertParametersToDestructuredObject ( context , actionName ) { Debug . assert ( actionName === refactorName , "Unexpected action name" ) ; const { file , startPosition , program , cancellationToken , host } = context ; const functionDeclaration = getFunctionDeclarationAtPosition ( file , startPosition , program . getTypeChecker ( ) ) ; if ( ! functionDeclaration || ! cancellationToken ) return undefined ; const groupedReferences = getGroupedReferences ( functionDeclaration , program , cancellationToken ) ; if ( groupedReferences . valid ) { const edits = textChanges . ChangeTracker . with ( context , t => doChange ( file , program , host , t , functionDeclaration , groupedReferences ) ) ; return { renameFilename : undefined , renameLocation : undefined , edits } ; } return { edits : [ ] } ; } function doChange ( sourceFile , program , host , changes , functionDeclaration , groupedReferences ) { const signature = groupedReferences . signature ; const newFunctionDeclarationParams = map ( createNewParameters ( functionDeclaration , program , host ) , param => getSynthesizedDeepClone ( param ) ) ; if ( signature ) { const newSignatureParams = map ( createNewParameters ( signature , program , host ) , param => getSynthesizedDeepClone ( param ) ) ; replaceParameters ( signature , newSignatureParams ) ; } replaceParameters ( functionDeclaration , newFunctionDeclarationParams ) ; const functionCalls = sortAndDeduplicate ( groupedReferences . functionCalls , ( a , b ) => compareValues ( a . pos , b . pos ) ) ; for ( const call of functionCalls ) { if ( call . arguments && call . arguments . length ) { const newArgument = getSynthesizedDeepClone ( createNewArgument ( functionDeclaration , call . arguments ) , true ) ; changes . replaceNodeRange ( getSourceFileOfNode ( call ) , first ( call . arguments ) , last ( call . arguments ) , newArgument , { leadingTriviaOption : textChanges . LeadingTriviaOption . IncludeAll , trailingTriviaOption : textChanges . TrailingTriviaOption . Include } ) ; } } function replaceParameters ( declarationOrSignature , parameterDeclarations ) { changes . replaceNodeRangeWithNodes ( sourceFile , first ( declarationOrSignature . parameters ) , last ( declarationOrSignature . parameters ) , parameterDeclarations , { joiner : ", " , indentation : 0 , leadingTriviaOption : textChanges . LeadingTriviaOption . IncludeAll , trailingTriviaOption : textChanges . TrailingTriviaOption . Include } ) ; } } function getGroupedReferences ( functionDeclaration , program , cancellationToken ) { const functionNames = getFunctionNames ( functionDeclaration ) ; const classNames = isConstructorDeclaration ( functionDeclaration ) ? getClassNames ( functionDeclaration ) : [ ] ; const names = deduplicate ( [ ... functionNames , ... classNames ] , equateValues ) ; const checker = program . getTypeChecker ( ) ; const references = flatMap ( names , name => FindAllReferences . getReferenceEntriesForNode ( - 1 , name , program , program . getSourceFiles ( ) , cancellationToken ) ) ; const groupedReferences = groupReferences ( references ) ; if ( ! every ( groupedReferences . declarations , decl => contains ( names , decl ) ) ) { groupedReferences . valid = false ; } return groupedReferences ; function groupReferences ( referenceEntries ) { const classReferences = { accessExpressions : [ ] , typeUsages : [ ] } ; const groupedReferences = { functionCalls : [ ] , declarations : [ ] , classReferences , valid : true } ; const functionSymbols = map ( functionNames , getSymbolTargetAtLocation ) ; const classSymbols = map ( classNames , getSymbolTargetAtLocation ) ; const isConstructor = isConstructorDeclaration ( functionDeclaration ) ; const contextualSymbols = map ( functionNames , name => getSymbolForContextualType ( name , checker ) ) ; for ( const entry of referenceEntries ) { if ( entry . kind === FindAllReferences . EntryKind . Span ) { groupedReferences . valid = false ; continue ; } if ( contains ( contextualSymbols , getSymbolTargetAtLocation ( entry . node ) ) ) { if ( isValidMethodSignature ( entry . node . parent ) ) { groupedReferences . signature = entry . node . parent ; continue ; } const call = entryToFunctionCall ( entry ) ; if ( call ) { groupedReferences . functionCalls . push ( call ) ; continue ; } } const contextualSymbol = getSymbolForContextualType ( entry . node , checker ) ; if ( contextualSymbol && contains ( contextualSymbols , contextualSymbol ) ) { const decl = entryToDeclaration ( entry ) ; if ( decl ) { groupedReferences . declarations . push ( decl ) ; continue ; } } if ( contains ( functionSymbols , getSymbolTargetAtLocation ( entry . node ) ) || isNewExpressionTarget ( entry . node ) ) { const importOrExportReference = entryToImportOrExport ( entry ) ; if ( importOrExportReference ) { continue ; } const decl = entryToDeclaration ( entry ) ; if ( decl ) { groupedReferences . declarations . push ( decl ) ; continue ; } const call = entryToFunctionCall ( entry ) ; if ( call ) { groupedReferences . functionCalls . push ( call ) ; continue ; } } if ( isConstructor && contains ( classSymbols , getSymbolTargetAtLocation ( entry . node ) ) ) { const importOrExportReference = entryToImportOrExport ( entry ) ; if ( importOrExportReference ) { continue ; } const decl = entryToDeclaration ( entry ) ; if ( decl ) { groupedReferences . declarations . push ( decl ) ; continue ; } const accessExpression = entryToAccessExpression ( entry ) ; if ( accessExpression ) { classReferences . accessExpressions . push ( accessExpression ) ; continue ; } if ( isClassDeclaration ( functionDeclaration . parent ) ) { const type = entryToType ( entry ) ; if ( type ) { classReferences . typeUsages . push ( type ) ; continue ; } } } groupedReferences . valid = false ; } return groupedReferences ; } function getSymbolTargetAtLocation ( node ) { const symbol = checker . getSymbolAtLocation ( node ) ; return symbol && getSymbolTarget ( symbol , checker ) ; } } /**
     * Gets the symbol for the contextual type of the node if it is not a union or intersection.
     */ function getSymbolForContextualType ( node , checker ) { const element = getContainingObjectLiteralElement ( node ) ; if ( element ) { const contextualType = checker . getContextualTypeForObjectLiteralElement ( element as ObjectLiteralElementLike ) ; const symbol = contextualType ?. getSymbol ( ) ; if ( symbol && ! ( getCheckFlags ( symbol ) & CheckFlags . Synthetic ) ) { return symbol ; } } } function entryToImportOrExport ( entry ) { const node = entry . node ; if ( isImportSpecifier ( node . parent ) || isImportClause ( node . parent ) || isImportEqualsDeclaration ( node . parent ) || isNamespaceImport ( node . parent ) ) { return node ; } if ( isExportSpecifier ( node . parent ) || isExportAssignment ( node . parent ) ) { return node ; } return undefined ; } function entryToDeclaration ( entry ) { if ( isDeclaration ( entry . node . parent ) ) { return entry . node ; } return undefined ; } function entryToFunctionCall ( entry ) { if ( entry . node . parent ) { const functionReference = entry . node ; const parent = functionReference . parent ; switch ( parent . kind ) { case SyntaxKind . CallExpression : case SyntaxKind . NewExpression : const callOrNewExpression = tryCast ( parent , isCallOrNewExpression ) ; if ( callOrNewExpression && callOrNewExpression . expression === functionReference ) { return callOrNewExpression ; } break ; case SyntaxKind . PropertyAccessExpression : const propertyAccessExpression = tryCast ( parent , isPropertyAccessExpression ) ; if ( propertyAccessExpression && propertyAccessExpression . parent && propertyAccessExpression . name === functionReference ) { const callOrNewExpression = tryCast ( propertyAccessExpression . parent , isCallOrNewExpression ) ; if ( callOrNewExpression && callOrNewExpression . expression === propertyAccessExpression ) { return callOrNewExpression ; } } break ; case SyntaxKind . ElementAccessExpression : const elementAccessExpression = tryCast ( parent , isElementAccessExpression ) ; if ( elementAccessExpression && elementAccessExpression . parent && elementAccessExpression . argumentExpression === functionReference ) { const callOrNewExpression = tryCast ( elementAccessExpression . parent , isCallOrNewExpression ) ; if ( callOrNewExpression && callOrNewExpression . expression === elementAccessExpression ) { return callOrNewExpression ; } } break ; } } return undefined ; } function entryToAccessExpression ( entry ) { if ( entry . node . parent ) { const reference = entry . node ; const parent = reference . parent ; switch ( parent . kind ) { case SyntaxKind . PropertyAccessExpression : const propertyAccessExpression = tryCast ( parent , isPropertyAccessExpression ) ; if ( propertyAccessExpression && propertyAccessExpression . expression === reference ) { return propertyAccessExpression ; } break ; case SyntaxKind . ElementAccessExpression : const elementAccessExpression = tryCast ( parent , isElementAccessExpression ) ; if ( elementAccessExpression && elementAccessExpression . expression === reference ) { return elementAccessExpression ; } break ; } } return undefined ; } function entryToType ( entry ) { const reference = entry . node ; if ( getMeaningFromLocation ( reference ) === SemanticMeaning . Type || isExpressionWithTypeArgumentsInClassExtendsClause ( reference . parent ) ) { return reference ; } return undefined ; } function getFunctionDeclarationAtPosition ( file , startPosition , checker ) { const node = getTouchingToken ( file , startPosition ) ; const functionDeclaration = getContainingFunctionDeclaration ( node ) ; if ( isTopLevelJSDoc ( node ) ) return undefined ; if ( functionDeclaration && isValidFunctionDeclaration ( functionDeclaration , checker ) && rangeContainsRange ( functionDeclaration , node ) && ! ( functionDeclaration . body && rangeContainsRange ( functionDeclaration . body , node ) ) ) return functionDeclaration ; return undefined ; } function isTopLevelJSDoc ( node ) { const containingJSDoc = findAncestor ( node , isJSDocNode ) ; if ( containingJSDoc ) { const containingNonJSDoc = findAncestor ( containingJSDoc , n => ! isJSDocNode ( n ) ) ; return ! ! containingNonJSDoc && isFunctionLikeDeclaration ( containingNonJSDoc ) ; } return false ; } function isValidMethodSignature ( node ) { return isMethodSignature ( node ) && ( isInterfaceDeclaration ( node . parent ) || isTypeLiteralNode ( node . parent ) ) ; } function isValidFunctionDeclaration ( functionDeclaration , checker ) { if ( ! isValidParameterNodeArray ( functionDeclaration . parameters , checker ) ) return false ; switch ( functionDeclaration . kind ) { case SyntaxKind . FunctionDeclaration : return hasNameOrDefault ( functionDeclaration ) && isSingleImplementation ( functionDeclaration , checker ) ; case SyntaxKind . MethodDeclaration : if ( isObjectLiteralExpression ( functionDeclaration . parent ) ) { const contextualSymbol = getSymbolForContextualType ( functionDeclaration . name , checker ) ; return contextualSymbol ?. declarations ?. length === 1 && isSingleImplementation ( functionDeclaration , checker ) ; } return isSingleImplementation ( functionDeclaration , checker ) ; case SyntaxKind . Constructor : if ( isClassDeclaration ( functionDeclaration . parent ) ) { return hasNameOrDefault ( functionDeclaration . parent ) && isSingleImplementation ( functionDeclaration , checker ) ; } else { return isValidVariableDeclaration ( functionDeclaration . parent . parent ) && isSingleImplementation ( functionDeclaration , checker ) ; } case SyntaxKind . FunctionExpression : case SyntaxKind . ArrowFunction : return isValidVariableDeclaration ( functionDeclaration . parent ) ; } return false ; } function isSingleImplementation ( functionDeclaration , checker ) { return ! ! functionDeclaration . body && ! checker . isImplementationOfOverload ( functionDeclaration ) ; } function hasNameOrDefault ( functionOrClassDeclaration ) { if ( ! functionOrClassDeclaration . name ) { const defaultKeyword = findModifier ( functionOrClassDeclaration , SyntaxKind . DefaultKeyword ) ; return ! ! defaultKeyword ; } return true ; } function isValidParameterNodeArray ( parameters , checker ) { return getRefactorableParametersLength ( parameters ) >= minimumParameterLength && every ( parameters , paramDecl => isValidParameterDeclaration ( paramDecl , checker ) ) ; } function isValidParameterDeclaration ( parameterDeclaration , checker ) { if ( isRestParameter ( parameterDeclaration ) ) { const type = checker . getTypeAtLocation ( parameterDeclaration ) ; if ( ! checker . isArrayType ( type ) && ! checker . isTupleType ( type ) ) return false ; } return ! parameterDeclaration . modifiers && ! parameterDeclaration . decorators && isIdentifier ( parameterDeclaration . name ) ; } function isValidVariableDeclaration ( node ) { return isVariableDeclaration ( node ) && isVarConst ( node ) && isIdentifier ( node . name ) && ! node . type ; } function hasThisParameter ( parameters ) { return parameters . length > 0 && isThis ( parameters [ 0 ] . name ) ; } function getRefactorableParametersLength ( parameters ) { if ( hasThisParameter ( parameters ) ) { return parameters . length - 1 ; } return parameters . length ; } function getRefactorableParameters ( parameters ) { if ( hasThisParameter ( parameters ) ) { parameters = factory . createNodeArray ( parameters . slice ( 1 ) , parameters . hasTrailingComma ) ; } return parameters ; } function createPropertyOrShorthandAssignment ( name , initializer ) { if ( isIdentifier ( initializer ) && getTextOfIdentifierOrLiteral ( initializer ) === name ) { return factory . createShorthandPropertyAssignment ( name ) ; } return factory . createPropertyAssignment ( name , initializer ) ; } function createNewArgument ( functionDeclaration , functionArguments ) { const parameters = getRefactorableParameters ( functionDeclaration . parameters ) ; const hasRestParameter = isRestParameter ( last ( parameters ) ) ; const nonRestArguments = hasRestParameter ? functionArguments . slice ( 0 , parameters . length - 1 ) : functionArguments ; const properties = map ( nonRestArguments , ( arg , i ) => { const parameterName = getParameterName ( parameters [ i ] ) ; const property = createPropertyOrShorthandAssignment ( parameterName , arg ) ; suppressLeadingAndTrailingTrivia ( property . name ) ; if ( isPropertyAssignment ( property ) ) suppressLeadingAndTrailingTrivia ( property . initializer ) ; copyComments ( arg , property ) ; return property ; } ) ; if ( hasRestParameter && functionArguments . length >= parameters . length ) { const restArguments = functionArguments . slice ( parameters . length - 1 ) ; const restProperty = factory . createPropertyAssignment ( getParameterName ( last ( parameters ) ) , factory . createArrayLiteralExpression ( restArguments ) ) ; properties . push ( restProperty ) ; } const objectLiteral = factory . createObjectLiteralExpression ( properties , false ) ; return objectLiteral ; } function createNewParameters ( functionDeclaration , program , host ) { const checker = program . getTypeChecker ( ) ; const refactorableParameters = getRefactorableParameters ( functionDeclaration . parameters ) ; const bindingElements = map ( refactorableParameters , createBindingElementFromParameterDeclaration ) ; const objectParameterName = factory . createObjectBindingPattern ( bindingElements ) ; const objectParameterType = createParameterTypeNode ( refactorableParameters ) ; let objectInitializer ; if ( every ( refactorableParameters , isOptionalParameter ) ) { objectInitializer = factory . createObjectLiteralExpression ( ) ; } const objectParameter = factory . createParameterDeclaration ( undefined , undefined , undefined , objectParameterName , undefined , objectParameterType , objectInitializer ) ; if ( hasThisParameter ( functionDeclaration . parameters ) ) { const thisParameter = functionDeclaration . parameters [ 0 ] ; const newThisParameter = factory . createParameterDeclaration ( undefined , undefined , undefined , thisParameter . name , undefined , thisParameter . type ) ; suppressLeadingAndTrailingTrivia ( newThisParameter . name ) ; copyComments ( thisParameter . name , newThisParameter . name ) ; if ( thisParameter . type ) { suppressLeadingAndTrailingTrivia ( newThisParameter . type ! ) ; copyComments ( thisParameter . type , newThisParameter . type ! ) ; } return factory . createNodeArray ( [ newThisParameter , objectParameter ] ) ; } return factory . createNodeArray ( [ objectParameter ] ) ; function createBindingElementFromParameterDeclaration ( parameterDeclaration ) { const element = factory . createBindingElement ( undefined , undefined , getParameterName ( parameterDeclaration ) , isRestParameter ( parameterDeclaration ) && isOptionalParameter ( parameterDeclaration ) ? factory . createArrayLiteralExpression ( ) : parameterDeclaration . initializer ) ; suppressLeadingAndTrailingTrivia ( element ) ; if ( parameterDeclaration . initializer && element . initializer ) { copyComments ( parameterDeclaration . initializer , element . initializer ) ; } return element ; } function createParameterTypeNode ( parameters ) { const members = map ( parameters , createPropertySignatureFromParameterDeclaration ) ; const typeNode = addEmitFlags ( factory . createTypeLiteralNode ( members ) , EmitFlags . SingleLine ) ; return typeNode ; } function createPropertySignatureFromParameterDeclaration ( parameterDeclaration ) { let parameterType = parameterDeclaration . type ; if ( ! parameterType && ( parameterDeclaration . initializer || isRestParameter ( parameterDeclaration ) ) ) { parameterType = getTypeNode ( parameterDeclaration ) ; } const propertySignature = factory . createPropertySignature ( undefined , getParameterName ( parameterDeclaration ) , isOptionalParameter ( parameterDeclaration ) ? factory . createToken ( SyntaxKind . QuestionToken ) : parameterDeclaration . questionToken , parameterType ) ; suppressLeadingAndTrailingTrivia ( propertySignature ) ; copyComments ( parameterDeclaration . name , propertySignature . name ) ; if ( parameterDeclaration . type && propertySignature . type ) { copyComments ( parameterDeclaration . type , propertySignature . type ) ; } return propertySignature ; } function getTypeNode ( node ) { const type = checker . getTypeAtLocation ( node ) ; return getTypeNodeIfAccessible ( type , node , program , host ) ; } function isOptionalParameter ( parameterDeclaration ) { if ( isRestParameter ( parameterDeclaration ) ) { const type = checker . getTypeAtLocation ( parameterDeclaration ) ; return ! checker . isTupleType ( type ) ; } return checker . isOptionalParameter ( parameterDeclaration ) ; } } function getParameterName ( paramDeclaration ) { return getTextOfIdentifierOrLiteral ( paramDeclaration . name ) ; } function getClassNames ( constructorDeclaration ) { switch ( constructorDeclaration . parent . kind ) { case SyntaxKind . ClassDeclaration : const classDeclaration = constructorDeclaration . parent ; if ( classDeclaration . name ) return [ classDeclaration . name ] ; const defaultModifier = Debug . checkDefined ( findModifier ( classDeclaration , SyntaxKind . DefaultKeyword ) , "Nameless class declaration should be a default export" ) ; return [ defaultModifier ] ; case SyntaxKind . ClassExpression : const classExpression = constructorDeclaration . parent ; const variableDeclaration = constructorDeclaration . parent . parent ; const className = classExpression . name ; if ( className ) return [ className , variableDeclaration . name ] ; return [ variableDeclaration . name ] ; } } function getFunctionNames ( functionDeclaration ) { switch ( functionDeclaration . kind ) { case SyntaxKind . FunctionDeclaration : if ( functionDeclaration . name ) return [ functionDeclaration . name ] ; const defaultModifier = Debug . checkDefined ( findModifier ( functionDeclaration , SyntaxKind . DefaultKeyword ) , "Nameless function declaration should be a default export" ) ; return [ defaultModifier ] ; case SyntaxKind . MethodDeclaration : return [ functionDeclaration . name ] ; case SyntaxKind . Constructor : const ctrKeyword = Debug . checkDefined ( findChildOfKind ( functionDeclaration , SyntaxKind . ConstructorKeyword , functionDeclaration . getSourceFile ( ) ) , "Constructor declaration should have constructor keyword" ) ; if ( functionDeclaration . parent . kind === SyntaxKind . ClassExpression ) { const variableDeclaration = functionDeclaration . parent . parent ; return [ variableDeclaration . name , ctrKeyword ] ; } return [ ctrKeyword ] ; case SyntaxKind . ArrowFunction : return [ functionDeclaration . parent . name ] ; case SyntaxKind . FunctionExpression : if ( functionDeclaration . name ) return [ functionDeclaration . name , functionDeclaration . parent . name ] ; return [ functionDeclaration . parent . name ] ; default : return Debug . assertNever ( functionDeclaration , `Unexpected function declaration kind ${ ( functionDeclaration as ValidFunctionDeclaration ) . kind }` ) ; } } type ValidParameterNodeArray = NodeArray < ValidParameterDeclaration > ; interface ValidVariableDeclaration extends VariableDeclaration { name : Identifier ; type : undefined ; } interface ValidConstructor extends ConstructorDeclaration { parent : ClassDeclaration | ( ClassExpression & { parent : ValidVariableDeclaration ; } ) ; parameters : NodeArray < ValidParameterDeclaration > ; body : FunctionBody ; } interface ValidFunction extends FunctionDeclaration { parameters : NodeArray < ValidParameterDeclaration > ; body : FunctionBody ; } interface ValidMethod extends MethodDeclaration { parameters : NodeArray < ValidParameterDeclaration > ; body : FunctionBody ; } interface ValidFunctionExpression extends FunctionExpression { parent : ValidVariableDeclaration ; parameters : NodeArray < ValidParameterDeclaration > ; } interface ValidArrowFunction extends ArrowFunction { parent : ValidVariableDeclaration ; parameters : NodeArray < ValidParameterDeclaration > ; } interface ValidMethodSignature extends MethodSignature { parameters : NodeArray < ValidParameterDeclaration > ; } type ValidFunctionDeclaration = ValidConstructor | ValidFunction | ValidMethod | ValidArrowFunction | ValidFunctionExpression ; interface ValidParameterDeclaration extends ParameterDeclaration { name : Identifier ; modifiers : undefined ; decorators : undefined ; } interface GroupedReferences { functionCalls : ( CallExpression | NewExpression ) [ ] ; declarations : Node [ ] ; signature ? : ValidMethodSignature ; classReferences ? : ClassReferences ; valid : boolean ; } interface ClassReferences { accessExpressions : Node [ ] ; typeUsages : Node [ ] ; } }