@@ -238,7 +238,7 @@ namespace ts.Completions {
238238
239239 function getStringLiteralCompletionEntries ( sourceFile : SourceFile , position : number , typeChecker : TypeChecker , compilerOptions : CompilerOptions , host : LanguageServiceHost , log : Log ) : CompletionInfo | undefined {
240240 const node = findPrecedingToken ( position , sourceFile ) ;
241- if ( ! node || ( node . kind !== SyntaxKind . StringLiteral && node . kind !== SyntaxKind . NoSubstitutionTemplateLiteral ) ) {
241+ if ( ! node || ! isStringLiteral ( node ) && ! isNoSubstitutionTemplateLiteral ( node ) ) {
242242 return undefined ;
243243 }
244244
@@ -277,21 +277,9 @@ namespace ts.Completions {
277277 // import x = require("/*completion position*/");
278278 // var y = require("/*completion position*/");
279279 // export * from "/*completion position*/";
280- const entries = PathCompletions . getStringLiteralCompletionsFromModuleNames ( < StringLiteral > node , compilerOptions , host , typeChecker ) ;
280+ const entries = PathCompletions . getStringLiteralCompletionsFromModuleNames ( node , compilerOptions , host , typeChecker ) ;
281281 return pathCompletionsInfo ( entries ) ;
282282 }
283- else if ( isEqualityExpression ( node . parent ) ) {
284- // Get completions from the type of the other operand
285- // i.e. switch (a) {
286- // case '/*completion position*/'
287- // }
288- return getStringLiteralCompletionEntriesFromType ( typeChecker . getTypeAtLocation ( node . parent . left === node ? node . parent . right : node . parent . left ) , typeChecker ) ;
289- }
290- else if ( isCaseOrDefaultClause ( node . parent ) ) {
291- // Get completions from the type of the switch expression
292- // i.e. x === '/*completion position'
293- return getStringLiteralCompletionEntriesFromType ( typeChecker . getTypeAtLocation ( ( < SwitchStatement > node . parent . parent . parent ) . expression ) , typeChecker ) ;
294- }
295283 else {
296284 const argumentInfo = SignatureHelp . getImmediatelyContainingArgumentInfo ( node , position , sourceFile ) ;
297285 if ( argumentInfo ) {
@@ -303,7 +291,7 @@ namespace ts.Completions {
303291
304292 // Get completion for string literal from string literal type
305293 // i.e. var x: "hi" | "hello" = "/*completion position*/"
306- return getStringLiteralCompletionEntriesFromType ( typeChecker . getContextualType ( < LiteralExpression > node ) , typeChecker ) ;
294+ return getStringLiteralCompletionEntriesFromType ( getContextualTypeFromParent ( node , typeChecker ) , typeChecker ) ;
307295 }
308296 }
309297
@@ -602,15 +590,60 @@ namespace ts.Completions {
602590 }
603591 type Request = { kind : "JsDocTagName" } | { kind : "JsDocTag" } | { kind : "JsDocParameterName" , tag : JSDocParameterTag } ;
604592
605- function getRecommendedCompletion ( currentToken : Node , checker : TypeChecker /*, symbolToOriginInfoMap: SymbolOriginInfoMap*/ ) : Symbol | undefined {
606- const ty = checker . getContextualType ( currentToken as Expression ) ;
593+ function getRecommendedCompletion ( currentToken : Node , checker : TypeChecker ) : Symbol | undefined {
594+ const ty = getContextualType ( currentToken , checker ) ;
607595 const symbol = ty && ty . symbol ;
608596 // Don't include make a recommended completion for an abstract class
609597 return symbol && ( symbol . flags & SymbolFlags . Enum || symbol . flags & SymbolFlags . Class && ! isAbstractConstructorSymbol ( symbol ) )
610598 ? getFirstSymbolInChain ( symbol , currentToken , checker )
611599 : undefined ;
612600 }
613601
602+ function getContextualType ( currentToken : Node , checker : ts . TypeChecker ) : Type | undefined {
603+ const { parent } = currentToken ;
604+ switch ( currentToken . kind ) {
605+ case ts . SyntaxKind . Identifier :
606+ return getContextualTypeFromParent ( currentToken as ts . Identifier , checker ) ;
607+ case ts . SyntaxKind . EqualsToken :
608+ return ts . isVariableDeclaration ( parent )
609+ ? checker . getContextualType ( parent . initializer )
610+ : ts . isBinaryExpression ( parent )
611+ ? checker . getTypeAtLocation ( parent . left )
612+ : undefined ;
613+ case ts . SyntaxKind . NewKeyword :
614+ return checker . getContextualType ( parent as ts . Expression ) ;
615+ case ts . SyntaxKind . CaseKeyword :
616+ return getSwitchedType ( cast ( currentToken . parent , isCaseClause ) , checker ) ;
617+ default :
618+ return isEqualityOperatorKind ( currentToken . kind ) && ts . isBinaryExpression ( parent ) && isEqualityOperatorKind ( parent . operatorToken . kind )
619+ // completion at `x ===/**/` should be for the right side
620+ ? checker . getTypeAtLocation ( parent . left )
621+ : checker . getContextualType ( currentToken as ts . Expression ) ;
622+ }
623+ }
624+
625+ function getContextualTypeFromParent ( node : ts . Expression , checker : ts . TypeChecker ) : Type | undefined {
626+ const { parent } = node ;
627+ switch ( parent . kind ) {
628+ case ts . SyntaxKind . NewExpression :
629+ return checker . getContextualType ( parent as ts . NewExpression ) ;
630+ case ts . SyntaxKind . BinaryExpression : {
631+ const { left, operatorToken, right } = parent as ts . BinaryExpression ;
632+ return isEqualityOperatorKind ( operatorToken . kind )
633+ ? checker . getTypeAtLocation ( node === right ? left : right )
634+ : checker . getContextualType ( node ) ;
635+ }
636+ case ts . SyntaxKind . CaseClause :
637+ return ( parent as ts . CaseClause ) . expression === node ? getSwitchedType ( parent as ts . CaseClause , checker ) : undefined ;
638+ default :
639+ return checker . getContextualType ( node ) ;
640+ }
641+ }
642+
643+ function getSwitchedType ( caseClause : ts . CaseClause , checker : ts . TypeChecker ) : ts . Type {
644+ return checker . getTypeAtLocation ( caseClause . parent . parent . expression ) ;
645+ }
646+
614647 function getFirstSymbolInChain ( symbol : Symbol , enclosingDeclaration : Node , checker : TypeChecker ) : Symbol | undefined {
615648 const chain = checker . getAccessibleSymbolChain ( symbol , enclosingDeclaration , /*meaning*/ SymbolFlags . All , /*useOnlyExternalAliasing*/ false ) ;
616649 if ( chain ) return first ( chain ) ;
@@ -851,7 +884,7 @@ namespace ts.Completions {
851884
852885 log ( "getCompletionData: Semantic work: " + ( timestamp ( ) - semanticStart ) ) ;
853886
854- const recommendedCompletion = getRecommendedCompletion ( previousToken , typeChecker ) ;
887+ const recommendedCompletion = previousToken && getRecommendedCompletion ( previousToken , typeChecker ) ;
855888 return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken } ;
856889
857890 type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag ;
@@ -2081,15 +2114,16 @@ namespace ts.Completions {
20812114 return isConstructorParameterCompletionKeyword ( stringToToken ( text ) ) ;
20822115 }
20832116
2084- function isEqualityExpression ( node : Node ) : node is BinaryExpression {
2085- return isBinaryExpression ( node ) && isEqualityOperatorKind ( node . operatorToken . kind ) ;
2086- }
2087-
2088- function isEqualityOperatorKind ( kind : SyntaxKind ) {
2089- return kind === SyntaxKind . EqualsEqualsToken ||
2090- kind === SyntaxKind . ExclamationEqualsToken ||
2091- kind === SyntaxKind . EqualsEqualsEqualsToken ||
2092- kind === SyntaxKind . ExclamationEqualsEqualsToken ;
2117+ function isEqualityOperatorKind ( kind : ts . SyntaxKind ) : kind is EqualityOperator {
2118+ switch ( kind ) {
2119+ case ts . SyntaxKind . EqualsEqualsEqualsToken :
2120+ case ts . SyntaxKind . EqualsEqualsToken :
2121+ case ts . SyntaxKind . ExclamationEqualsEqualsToken :
2122+ case ts . SyntaxKind . ExclamationEqualsToken :
2123+ return true ;
2124+ default :
2125+ return false ;
2126+ }
20932127 }
20942128
20952129 /** Get the corresponding JSDocTag node if the position is in a jsDoc comment */
0 commit comments