namespace ts . codefix { type AcceptedDeclaration = ParameterPropertyDeclaration | PropertyDeclaration | PropertyAssignment ; type AcceptedNameType = Identifier | StringLiteral ; type ContainerDeclaration = ClassLikeDeclaration | ObjectLiteralExpression ; type Info = AccessorInfo | refactor . RefactorErrorInfo ; interface AccessorInfo { readonly container : ContainerDeclaration ; readonly isStatic : boolean ; readonly isReadonly : boolean ; readonly type : TypeNode | undefined ; readonly declaration : AcceptedDeclaration ; readonly fieldName : AcceptedNameType ; readonly accessorName : AcceptedNameType ; readonly originalName : string ; readonly renameAccessor : boolean ; } export function generateAccessorFromProperty ( file , program , start , end , context , _actionName ) { const fieldInfo = getAccessorConvertiblePropertyAtPosition ( file , program , start , end ) ; if ( ! fieldInfo || refactor . isRefactorErrorInfo ( fieldInfo ) ) return undefined ; const changeTracker = textChanges . ChangeTracker . fromContext ( context ) ; const { isStatic , isReadonly , fieldName , accessorName , originalName , type , container , declaration } = fieldInfo ; suppressLeadingAndTrailingTrivia ( fieldName ) ; suppressLeadingAndTrailingTrivia ( accessorName ) ; suppressLeadingAndTrailingTrivia ( declaration ) ; suppressLeadingAndTrailingTrivia ( container ) ; let accessorModifiers ; let fieldModifiers ; if ( isClassLike ( container ) ) { const modifierFlags = getEffectiveModifierFlags ( declaration ) ; if ( isSourceFileJS ( file ) ) { const modifiers = createModifiers ( modifierFlags ) ; accessorModifiers = modifiers ; fieldModifiers = modifiers ; } else { accessorModifiers = createModifiers ( prepareModifierFlagsForAccessor ( modifierFlags ) ) ; fieldModifiers = createModifiers ( prepareModifierFlagsForField ( modifierFlags ) ) ; } } updateFieldDeclaration ( changeTracker , file , declaration , type , fieldName , fieldModifiers ) ; const getAccessor = generateGetAccessor ( fieldName , accessorName , type , accessorModifiers , isStatic , container ) ; suppressLeadingAndTrailingTrivia ( getAccessor ) ; insertAccessor ( changeTracker , file , getAccessor , declaration , container ) ; if ( isReadonly ) { const constructor = getFirstConstructorWithBody ( container as ClassLikeDeclaration ) ; if ( constructor ) { updateReadonlyPropertyInitializerStatementConstructor ( changeTracker , file , constructor , fieldName . text , originalName ) ; } } else { const setAccessor = generateSetAccessor ( fieldName , accessorName , type , accessorModifiers , isStatic , container ) ; suppressLeadingAndTrailingTrivia ( setAccessor ) ; insertAccessor ( changeTracker , file , setAccessor , declaration , container ) ; } return changeTracker . getChanges ( ) ; } function isConvertibleName ( name ) { return isIdentifier ( name ) || isStringLiteral ( name ) ; } function isAcceptedDeclaration ( node ) { return isParameterPropertyDeclaration ( node , node . parent ) || isPropertyDeclaration ( node ) || isPropertyAssignment ( node ) ; } function createPropertyName ( name , originalName ) { return isIdentifier ( originalName ) ? factory . createIdentifier ( name ) : factory . createStringLiteral ( name ) ; } function createAccessorAccessExpression ( fieldName , isStatic , container ) { const leftHead = isStatic ? ( container as ClassLikeDeclaration ) . name ! : factory . createThis ( ) ; return isIdentifier ( fieldName ) ? factory . createPropertyAccessExpression ( leftHead , fieldName ) : factory . createElementAccessExpression ( leftHead , factory . createStringLiteralFromNode ( fieldName ) ) ; } function prepareModifierFlagsForAccessor ( modifierFlags ) { modifierFlags &= ~ ModifierFlags . Readonly ; modifierFlags &= ~ ModifierFlags . Private ; if ( ! ( modifierFlags & ModifierFlags . Protected ) ) { modifierFlags |= ModifierFlags . Public ; } return modifierFlags ; } function prepareModifierFlagsForField ( modifierFlags ) { modifierFlags &= ~ ModifierFlags . Public ; modifierFlags &= ~ ModifierFlags . Protected ; modifierFlags |= ModifierFlags . Private ; return modifierFlags ; } export function getAccessorConvertiblePropertyAtPosition ( file , program , start , end , considerEmptySpans = true ) { const node = getTokenAtPosition ( file , start ) ; const cursorRequest = start === end && considerEmptySpans ; const declaration = findAncestor ( node . parent , isAcceptedDeclaration ) ; const meaning = ModifierFlags . AccessibilityModifier | ModifierFlags . Static | ModifierFlags . Readonly ; if ( ! declaration || ( ! ( nodeOverlapsWithStartEnd ( declaration . name , file , start , end ) || cursorRequest ) ) ) { return { error : getLocaleSpecificMessage ( Diagnostics . Could_not_find_property_for_which_to_generate_accessor ) } ; } if ( ! isConvertibleName ( declaration . name ) ) { return { error : getLocaleSpecificMessage ( Diagnostics . Name_is_not_valid ) } ; } if ( ( getEffectiveModifierFlags ( declaration ) | meaning ) !== meaning ) { return { error : getLocaleSpecificMessage ( Diagnostics . Can_only_convert_property_with_modifier ) } ; } const name = declaration . name . text ; const startWithUnderscore = startsWithUnderscore ( name ) ; const fieldName = createPropertyName ( startWithUnderscore ? name : getUniqueName ( `_${ name }` , file ) , declaration . name ) ; const accessorName = createPropertyName ( startWithUnderscore ? getUniqueName ( name . substring ( 1 ) , file ) : name , declaration . name ) ; return { isStatic : hasStaticModifier ( declaration ) , isReadonly : hasEffectiveReadonlyModifier ( declaration ) , type : getDeclarationType ( declaration , program ) , container : declaration . kind === SyntaxKind . Parameter ? declaration . parent . parent : declaration . parent , originalName : ( declaration . name as AcceptedNameType ) . text , declaration , fieldName , accessorName , renameAccessor : startWithUnderscore } ; } function generateGetAccessor ( fieldName , accessorName , type , modifiers , isStatic , container ) { return factory . createGetAccessorDeclaration ( undefined , modifiers , accessorName , undefined ! , type , factory . createBlock ( [ factory . createReturnStatement ( createAccessorAccessExpression ( fieldName , isStatic , container ) ) ] , true ) ) ; } function generateSetAccessor ( fieldName , accessorName , type , modifiers , isStatic , container ) { return factory . createSetAccessorDeclaration ( undefined , modifiers , accessorName , [ factory . createParameterDeclaration ( undefined , undefined , undefined , factory . createIdentifier ( "value" ) , undefined , type ) ] , factory . createBlock ( [ factory . createExpressionStatement ( factory . createAssignment ( createAccessorAccessExpression ( fieldName , isStatic , container ) , factory . createIdentifier ( "value" ) ) ) ] , true ) ) ; } function updatePropertyDeclaration ( changeTracker , file , declaration , type , fieldName , modifiers ) { const property = factory . updatePropertyDeclaration ( declaration , declaration . decorators , modifiers , fieldName , declaration . questionToken || declaration . exclamationToken , type , declaration . initializer ) ; changeTracker . replaceNode ( file , declaration , property ) ; } function updatePropertyAssignmentDeclaration ( changeTracker , file , declaration , fieldName ) { const assignment = factory . updatePropertyAssignment ( declaration , fieldName , declaration . initializer ) ; changeTracker . replacePropertyAssignment ( file , declaration , assignment ) ; } function updateFieldDeclaration ( changeTracker , file , declaration , type , fieldName , modifiers ) { if ( isPropertyDeclaration ( declaration ) ) { updatePropertyDeclaration ( changeTracker , file , declaration , type , fieldName , modifiers ) ; } else if ( isPropertyAssignment ( declaration ) ) { updatePropertyAssignmentDeclaration ( changeTracker , file , declaration , fieldName ) ; } else { changeTracker . replaceNode ( file , declaration , factory . updateParameterDeclaration ( declaration , declaration . decorators , modifiers , declaration . dotDotDotToken , cast ( fieldName , isIdentifier ) , declaration . questionToken , declaration . type , declaration . initializer ) ) ; } } function insertAccessor ( changeTracker , file , accessor , declaration , container ) { isParameterPropertyDeclaration ( declaration , declaration . parent ) ? changeTracker . insertNodeAtClassStart ( file , container as ClassLikeDeclaration , accessor ) : isPropertyAssignment ( declaration ) ? changeTracker . insertNodeAfterComma ( file , declaration , accessor ) : changeTracker . insertNodeAfter ( file , declaration , accessor ) ; } function updateReadonlyPropertyInitializerStatementConstructor ( changeTracker , file , constructor , fieldName , originalName ) { if ( ! constructor . body ) return ; constructor . body . forEachChild ( function recur ( node ) { if ( isElementAccessExpression ( node ) && node . expression . kind === SyntaxKind . ThisKeyword && isStringLiteral ( node . argumentExpression ) && node . argumentExpression . text === originalName && isWriteAccess ( node ) ) { changeTracker . replaceNode ( file , node . argumentExpression , factory . createStringLiteral ( fieldName ) ) ; } if ( isPropertyAccessExpression ( node ) && node . expression . kind === SyntaxKind . ThisKeyword && node . name . text === originalName && isWriteAccess ( node ) ) { changeTracker . replaceNode ( file , node . name , factory . createIdentifier ( fieldName ) ) ; } if ( ! isFunctionLike ( node ) && ! isClassLike ( node ) ) { node . forEachChild ( recur ) ; } } ) ; } function getDeclarationType ( declaration , program ) { const typeNode = getTypeAnnotationNode ( declaration ) ; if ( isPropertyDeclaration ( declaration ) && typeNode && declaration . questionToken ) { const typeChecker = program . getTypeChecker ( ) ; const type = typeChecker . getTypeFromTypeNode ( typeNode ) ; if ( ! typeChecker . isTypeAssignableTo ( typeChecker . getUndefinedType ( ) , type ) ) { const types = isUnionTypeNode ( typeNode ) ? typeNode . types : [ typeNode ] ; return factory . createUnionTypeNode ( [ ... types , factory . createKeywordTypeNode ( SyntaxKind . UndefinedKeyword ) ] ) ; } } return typeNode ; } export function getAllSupers ( decl , checker ) { const res = [ ] ; while ( decl ) { const superElement = getClassExtendsHeritageElement ( decl ) ; const superSymbol = superElement && checker . getSymbolAtLocation ( superElement . expression ) ; if ( ! superSymbol ) break ; const symbol = superSymbol . flags & SymbolFlags . Alias ? checker . getAliasedSymbol ( superSymbol ) : superSymbol ; const superDecl = symbol . declarations && find ( symbol . declarations , isClassLike ) ; if ( ! superDecl ) break ; res . push ( superDecl ) ; decl = superDecl ; } return res ; } export type ClassOrInterface = ClassLikeDeclaration | InterfaceDeclaration ; }