@@ -20,14 +20,18 @@ export default (
20
20
const typeChecker = languageService . getProgram ( ) ! . getTypeChecker ( ) !
21
21
const objType = typeChecker . getContextualType ( node )
22
22
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
+ } )
25
29
for ( const property of properties ) {
26
30
const entry = entries . find ( ( { name } ) => name === property . name )
27
31
if ( ! entry ) continue
28
32
const type = typeChecker . getTypeOfSymbolAtLocation ( property , node )
29
33
if ( ! type ) continue
30
- if ( isMethodCompletionCall ( type , typeChecker ) ) {
34
+ if ( isFunctionType ( type , typeChecker ) ) {
31
35
if ( [ 'above' , 'remove' ] . includes ( keepOriginal ) && preferences . includeCompletionsWithObjectLiteralMethodSnippets ) {
32
36
const methodEntryIndex = entries . findIndex ( e => e . name === entry . name && isObjectLiteralMethodSnippet ( e ) )
33
37
const methodEntry = entries [ methodEntryIndex ]
@@ -79,35 +83,43 @@ const isObjectLiteralMethodSnippet = (entry: ts.CompletionEntry) => {
79
83
return detail ?. startsWith ( '(' ) && detail . split ( '\n' ) [ 0 ] ! . trimEnd ( ) . endsWith ( ')' )
80
84
}
81
85
82
- const isMethodCompletionCall = ( type : ts . Type , checker : ts . TypeChecker ) => {
86
+ const isFunctionType = ( type : ts . Type , checker : ts . TypeChecker ) => {
83
87
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
+ } )
85
97
}
86
98
87
99
const isStringCompletion = ( type : ts . Type ) => {
88
- if ( type . flags & ts . TypeFlags . Undefined ) return true
100
+ if ( type . flags & ts . TypeFlags . Undefined ) return false
89
101
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 ) )
91
103
return false
92
104
}
93
105
94
106
const isArrayCompletion = ( type : ts . Type , checker : ts . TypeChecker ) => {
95
107
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
97
109
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 ) )
99
111
return false
100
112
}
101
113
102
114
const 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
104
116
if ( checker [ 'isArrayLikeType' ] ( type ) ) return false
105
117
if ( type . flags & ts . TypeFlags . Object ) {
106
118
if ( ( type as ts . ObjectType ) . objectFlags & ts . ObjectFlags . Class ) return false
107
119
// complete with regexp?
108
120
if ( type . symbol ?. escapedName === 'RegExp' ) return false
109
121
return true
110
122
}
111
- if ( type . isUnion ( ) ) return type . types . every ( type => isObjectCompletion ( type , checker ) )
123
+ if ( type . isUnion ( ) ) return isEverySubtype ( type , type => isObjectCompletion ( type , checker ) )
112
124
return false
113
125
}
0 commit comments