@@ -7,9 +7,9 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
77 const node = findChildContainingExactPosition ( sourceFile , position )
88 if ( ! node || isTypeNode ( node ) ) return
99
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 )
1313 if ( signatures . length === 0 ) return
1414 const signature = signatures [ 0 ]
1515 if ( signatures . length > 1 && c ( 'methodSnippets.multipleSignatures' ) === 'empty' ) {
@@ -23,6 +23,7 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
2323 // Investigate merging signatures
2424 const { parameters } = signatures [ 0 ] !
2525 const printer = ts . createPrinter ( )
26+ let isVoidOrNotMap : boolean [ ] = [ ]
2627 const paramsToInsert = compact (
2728 ( skipMode === 'all' ? [ ] : parameters ) . map ( param => {
2829 const valueDeclaration = param . valueDeclaration as ts . ParameterDeclaration | undefined
@@ -36,6 +37,18 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
3637 if ( isOptional ) return undefined
3738 break
3839 }
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+ )
3952 const insertName = insertMode === 'always-name' || ! valueDeclaration
4053 const insertText = insertName
4154 ? param . name
@@ -63,8 +76,15 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
6376 const allFiltered = paramsToInsert . length === 0 && parameters . length > paramsToInsert . length
6477 if ( allFiltered ) return [ '' ]
6578
79+ const lastNonVoidIndex = isVoidOrNotMap . lastIndexOf ( false )
80+ if ( lastNonVoidIndex !== - 1 ) {
81+ isVoidOrNotMap = [ ...repeatItems ( false , lastNonVoidIndex + 1 ) , .../* true */ isVoidOrNotMap . slice ( lastNonVoidIndex + 1 ) ]
82+ }
83+
6684 // 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 ] )
6888 // return `(${paramsToInsert.map((param, i) => `\${${i + 1}:${param.replaceAll}}`).join(', ') })`
6989
7090 function cloneBindingName ( node : ts . BindingName ) : ts . BindingName {
@@ -87,4 +107,17 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
87107 return ts . setEmitFlags ( visited , ts . EmitFlags . SingleLine | ts . EmitFlags . NoAsciiEscaping )
88108 }
89109 }
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 ] !
90123}
0 commit comments