@@ -66,7 +66,8 @@ export default (prior: ts.CompletionInfo): ts.CompletionEntry[] | void => {
6666 const insertSnippetVariant = completingStyleMap . find ( ( [ , detector ] ) => detector ( type ! , typeChecker ) ) ?. [ 0 ] ?? fallbackSnippet
6767 if ( ! insertSnippetVariant ) continue
6868 const [ insertSnippetText , insertSnippetPreview ] = typeof insertSnippetVariant === 'function' ? insertSnippetVariant ( ) : insertSnippetVariant
69- const insertText = insertTextAfterEntry ( entry , insertSnippetText )
69+ let insertText = insertTextAfterEntry ( entry , insertSnippetText )
70+ if ( node . getSourceFile ( ) . getFullText ( ) [ position ] === ',' ) insertText = insertText . slice ( 0 , - 1 )
7071 const index = entries . indexOf ( entry )
7172 entries . splice ( index + ( keepOriginal === 'before' ? 1 : 0 ) , keepOriginal === 'remove' ? 1 : 0 , {
7273 ...entry ,
@@ -100,10 +101,20 @@ const isEverySubtype = (type: ts.UnionType, predicate: (type: ts.Type) => boolea
100101 } )
101102}
102103
103- const isStringCompletion = ( type : ts . Type ) => {
104+ const isStringCompletion = ( type : ts . Type , checker : ts . TypeChecker ) => {
104105 if ( type . flags & ts . TypeFlags . Undefined ) return false
105106 if ( type . flags & ts . TypeFlags . StringLike ) return true
106- if ( type . isUnion ( ) ) return isEverySubtype ( type , type => isStringCompletion ( type ) )
107+ if ( type . isUnion ( ) )
108+ return isEverySubtype (
109+ type ,
110+ type =>
111+ isStringCompletion ( type , checker ) ||
112+ // string & {} for string complete
113+ ( type . isIntersection ( ) &&
114+ type . types . length === 2 &&
115+ type . types [ 0 ] ! . flags & ts . TypeFlags . String &&
116+ checker [ 'isEmptyAnonymousObjectType' ] ( type . types [ 1 ] ) ) ,
117+ )
107118 return false
108119}
109120
0 commit comments