@@ -5,8 +5,52 @@ export default {
55 codes : [ 2339 ] ,
66 kind : 'quickfix' ,
77 title : 'Declare missing property' ,
8- tryToApply ( { sourceFile, node } ) {
8+ tryToApply ( { sourceFile, node, c , languageService } ) {
99 const param = matchParents ( node , [ 'Identifier' , 'BindingElement' , 'ObjectBindingPattern' , 'Parameter' ] )
10+ const objAccess = matchParents ( node , [ 'Identifier' , 'PropertyAccessExpression' ] )
11+ const missingPropName = ( node as ts . Identifier ) . text
12+ if ( objAccess ) {
13+ const checker = languageService . getProgram ( ) ! . getTypeChecker ( ) !
14+ const type = checker . getContextualType ( objAccess . expression ) || checker . getTypeAtLocation ( objAccess . expression )
15+ const props = type
16+ . getProperties ( )
17+ . map ( type => {
18+ const node = type . declarations ?. find ( declaration => {
19+ return c ( 'declareMissingPropertyQuickfixOtherFiles' ) || declaration . getSourceFile ( ) . fileName === sourceFile . fileName
20+ } )
21+ if ( node === undefined ) return undefined !
22+ return { name : type . name , node }
23+ } )
24+ . filter ( Boolean )
25+ // TARGET PROP
26+ const propInsertAfter = props . find ( prop => missingPropName . startsWith ( prop . name ) ) ?? props . at ( - 1 )
27+ if ( propInsertAfter ) {
28+ const propInsertParent = propInsertAfter . node . parent
29+ const sameParentLiteralProps = props . filter (
30+ prop => prop . node . parent === propInsertParent && ts . isPropertyAssignment ( prop . node ) && ! ts . isIdentifier ( prop . node . initializer ) ,
31+ )
32+ const insertObject =
33+ sameParentLiteralProps . length > 0 &&
34+ sameParentLiteralProps . every ( sameParentProp => ts . isObjectLiteralExpression ( ( sameParentProp . node as ts . PropertyAssignment ) . initializer ) )
35+ const insertPos = propInsertAfter . node . end
36+ const insertComma = sourceFile . getFullText ( ) . slice ( insertPos - 1 , insertPos ) !== ','
37+ const getLine = pos => sourceFile . getLineAndCharacterOfPosition ( pos ) . line
38+ const insertNewLine = getLine ( propInsertAfter . node . pos ) !== getLine ( propInsertAfter . node . end )
39+ const insertText = `${ insertComma ? ',' : '' } ${ insertNewLine ? '\n' : ' ' } ${ missingPropName } `
40+ const snippet = insertObject ? `: {${ insertNewLine ? '\n\t' : '' } $0${ insertNewLine ? '\n' : '' } }` : `$0`
41+ return {
42+ snippetEdits : [
43+ {
44+ newText : `${ tsFull . escapeSnippetText ( insertText ) } ${ snippet } ` ,
45+ span : {
46+ length : 0 ,
47+ start : insertPos ,
48+ } ,
49+ } ,
50+ ] ,
51+ }
52+ }
53+ }
1054 if ( param ) {
1155 // special react pattern
1256 if ( ts . isArrowFunction ( param . parent ) && ts . isVariableDeclaration ( param . parent . parent ) ) {
@@ -20,7 +64,7 @@ export default {
2064 const hasMembers = param . type . members . length > 0
2165 const insertPos = param . type . members . at ( - 1 ) ?. end ?? param . type . end - 1
2266 const insertComma = hasMembers && sourceFile . getFullText ( ) . slice ( insertPos - 1 , insertPos ) !== ','
23- let insertText = ( node as ts . Identifier ) . text
67+ let insertText = missingPropName
2468 if ( insertComma ) insertText = `, ${ insertText } `
2569 // alternatively only one snippetEdit could be used with tsFull.escapeSnippetText(insertText) + $0
2670 return {
0 commit comments