@@ -7,9 +7,9 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
7
7
const node = findChildContainingExactPosition ( sourceFile , position )
8
8
if ( ! node || isTypeNode ( node ) ) return
9
9
10
- const typeChecker = languageService . getProgram ( ) ! . getTypeChecker ( ) !
11
- const type = typeChecker . getTypeAtLocation ( node )
12
- const signatures = typeChecker . getSignaturesOfType ( type , ts . SignatureKind . Call )
10
+ const checker = languageService . getProgram ( ) ! . getTypeChecker ( ) !
11
+ const type = checker . getTypeAtLocation ( node )
12
+ const signatures = checker . getSignaturesOfType ( type , ts . SignatureKind . Call )
13
13
if ( signatures . length === 0 ) return
14
14
const signature = signatures [ 0 ]
15
15
if ( signatures . length > 1 && c ( 'methodSnippets.multipleSignatures' ) === 'empty' ) {
@@ -23,6 +23,7 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
23
23
// Investigate merging signatures
24
24
const { parameters } = signatures [ 0 ] !
25
25
const printer = ts . createPrinter ( )
26
+ let isVoidOrNotMap : boolean [ ] = [ ]
26
27
const paramsToInsert = compact (
27
28
( skipMode === 'all' ? [ ] : parameters ) . map ( param => {
28
29
const valueDeclaration = param . valueDeclaration as ts . ParameterDeclaration | undefined
@@ -36,6 +37,18 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
36
37
if ( isOptional ) return undefined
37
38
break
38
39
}
40
+ const voidType = ( checker as unknown as FullChecker ) . getVoidType ( )
41
+ const parameterType = valueDeclaration && checker . getTypeOfSymbolAtLocation ( param , valueDeclaration )
42
+ isVoidOrNotMap . push (
43
+ ! ! (
44
+ parameterType &&
45
+ ( parameterType === voidType ||
46
+ // new Promise<void> resolve type
47
+ ( parameterType . isUnion ( ) &&
48
+ parameterType . types [ 0 ] === voidType &&
49
+ getPromiseLikeTypeArgument ( parameterType . types [ 1 ] , checker ) === voidType ) )
50
+ ) ,
51
+ )
39
52
const insertName = insertMode === 'always-name' || ! valueDeclaration
40
53
const insertText = insertName
41
54
? param . name
@@ -63,8 +76,15 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
63
76
const allFiltered = paramsToInsert . length === 0 && parameters . length > paramsToInsert . length
64
77
if ( allFiltered ) return [ '' ]
65
78
79
+ const lastNonVoidIndex = isVoidOrNotMap . lastIndexOf ( false )
80
+ if ( lastNonVoidIndex !== - 1 ) {
81
+ isVoidOrNotMap = [ ...repeatItems ( false , lastNonVoidIndex + 1 ) , .../* true */ isVoidOrNotMap . slice ( lastNonVoidIndex + 1 ) ]
82
+ }
83
+
66
84
// methodSnippets.replaceArguments is processed with last stage in onCompletionAccepted
67
- return paramsToInsert
85
+
86
+ // do natural, final filtering
87
+ return paramsToInsert . filter ( ( _x , i ) => ! isVoidOrNotMap [ i ] )
68
88
// return `(${paramsToInsert.map((param, i) => `\${${i + 1}:${param.replaceAll}}`).join(', ') })`
69
89
70
90
function cloneBindingName ( node : ts . BindingName ) : ts . BindingName {
@@ -87,4 +107,17 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
87
107
return ts . setEmitFlags ( visited , ts . EmitFlags . SingleLine | ts . EmitFlags . NoAsciiEscaping )
88
108
}
89
109
}
110
+
111
+ function repeatItems < T > ( item : T , count : number ) : T [ ] {
112
+ return Array . from ( { length : count } ) . map ( ( ) => item )
113
+ }
114
+ }
115
+
116
+ function getPromiseLikeTypeArgument ( type : ts . Type | undefined , checker : ts . TypeChecker ) {
117
+ if ( ! type ) return
118
+ if ( ! ( type . flags & ts . TypeFlags . Object ) || ! ( ( type as ts . ObjectType ) . objectFlags & ts . ObjectFlags . Reference ) ) return
119
+ if ( type . symbol . name !== 'PromiseLike' ) return
120
+ const typeArgs = checker . getTypeArguments ( type as ts . TypeReference )
121
+ if ( typeArgs . length !== 1 ) return
122
+ return typeArgs [ 0 ] !
90
123
}
0 commit comments