@@ -20,14 +20,18 @@ export default (
2020 const typeChecker = languageService . getProgram ( ) ! . getTypeChecker ( ) !
2121 const objType = typeChecker . getContextualType ( node )
2222 if ( ! objType ) return
23- // its doesn't return all actual properties in some cases e.g. it would be more correct to use symbols from entries, but there is a block from TS
24- const properties = objType . getProperties ( )
23+ const types = objType . isUnion ( ) ? objType . types : [ objType ]
24+ const properties = types . flatMap ( type => {
25+ if ( isFunctionType ( type , typeChecker ) ) return [ ]
26+ if ( isObjectCompletion ( type , typeChecker ) ) return typeChecker . getPropertiesOfType ( type )
27+ return [ ]
28+ } )
2529 for ( const property of properties ) {
2630 const entry = entries . find ( ( { name } ) => name === property . name )
2731 if ( ! entry ) continue
2832 const type = typeChecker . getTypeOfSymbolAtLocation ( property , node )
2933 if ( ! type ) continue
30- if ( isMethodCompletionCall ( type , typeChecker ) ) {
34+ if ( isFunctionType ( type , typeChecker ) ) {
3135 if ( [ 'above' , 'remove' ] . includes ( keepOriginal ) && preferences . includeCompletionsWithObjectLiteralMethodSnippets ) {
3236 const methodEntryIndex = entries . findIndex ( e => e . name === entry . name && isObjectLiteralMethodSnippet ( e ) )
3337 const methodEntry = entries [ methodEntryIndex ]
@@ -79,35 +83,43 @@ const isObjectLiteralMethodSnippet = (entry: ts.CompletionEntry) => {
7983 return detail ?. startsWith ( '(' ) && detail . split ( '\n' ) [ 0 ] ! . trimEnd ( ) . endsWith ( ')' )
8084}
8185
82- const isMethodCompletionCall = ( type : ts . Type , checker : ts . TypeChecker ) => {
86+ const isFunctionType = ( type : ts . Type , checker : ts . TypeChecker ) => {
8387 if ( checker . getSignaturesOfType ( type , ts . SignatureKind . Call ) . length > 0 ) return true
84- if ( type . isUnion ( ) ) return type . types . some ( type => isMethodCompletionCall ( type , checker ) )
88+ if ( type . isUnion ( ) ) return type . types . some ( type => isFunctionType ( type , checker ) )
89+ }
90+
91+ const isEverySubtype = ( type : ts . UnionType , predicate : ( type : ts . Type ) => boolean ) : boolean => {
92+ // union cannot consist of only undefined types
93+ return type . types . every ( type => {
94+ if ( type . flags & ts . TypeFlags . Undefined ) return true
95+ return predicate ( type )
96+ } )
8597}
8698
8799const isStringCompletion = ( type : ts . Type ) => {
88- if ( type . flags & ts . TypeFlags . Undefined ) return true
100+ if ( type . flags & ts . TypeFlags . Undefined ) return false
89101 if ( type . flags & ts . TypeFlags . StringLike ) return true
90- if ( type . isUnion ( ) ) return type . types . every ( type => isStringCompletion ( type ) )
102+ if ( type . isUnion ( ) ) return isEverySubtype ( type , type => isStringCompletion ( type ) )
91103 return false
92104}
93105
94106const isArrayCompletion = ( type : ts . Type , checker : ts . TypeChecker ) => {
95107 if ( type . flags & ts . TypeFlags . Any ) return false
96- if ( type . flags & ts . TypeFlags . Undefined ) return true
108+ if ( type . flags & ts . TypeFlags . Undefined ) return false
97109 if ( checker [ 'isArrayLikeType' ] ( type ) ) return true
98- if ( type . isUnion ( ) ) return type . types . every ( type => isArrayCompletion ( type , checker ) )
110+ if ( type . isUnion ( ) ) return isEverySubtype ( type , type => isArrayCompletion ( type , checker ) )
99111 return false
100112}
101113
102114const isObjectCompletion = ( type : ts . Type , checker : ts . TypeChecker ) => {
103- if ( type . flags & ts . TypeFlags . Undefined ) return true
115+ if ( type . flags & ts . TypeFlags . Undefined ) return false
104116 if ( checker [ 'isArrayLikeType' ] ( type ) ) return false
105117 if ( type . flags & ts . TypeFlags . Object ) {
106118 if ( ( type as ts . ObjectType ) . objectFlags & ts . ObjectFlags . Class ) return false
107119 // complete with regexp?
108120 if ( type . symbol ?. escapedName === 'RegExp' ) return false
109121 return true
110122 }
111- if ( type . isUnion ( ) ) return type . types . every ( type => isObjectCompletion ( type , checker ) )
123+ if ( type . isUnion ( ) ) return isEverySubtype ( type , type => isObjectCompletion ( type , checker ) )
112124 return false
113125}
0 commit comments