diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0354f40c749..9613faa6584 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -85,8 +85,7 @@ module ts { getDiagnostics, getDeclarationDiagnostics, getGlobalDiagnostics, - getParentOfSymbol, - getNarrowedTypeOfSymbol, + getTypeOfSymbolAtLocation, getDeclaredTypeOfSymbol, getPropertiesOfType, getPropertyOfType, @@ -94,9 +93,9 @@ module ts { getIndexTypeOfType, getReturnTypeOfSignature, getSymbolsInScope, - getSymbolInfo, + getSymbolAtLocation, getShorthandAssignmentValueSymbol, - getTypeOfNode, + getTypeAtLocation, typeToString, getSymbolDisplayBuilder, symbolToString, @@ -4391,6 +4390,46 @@ module ts { } } + function resolveLocation(node: Node) { + // Resolve location from top down towards node if it is a context sensitive expression + // That helps in making sure not assigning types as any when resolved out of order + var containerNodes: Node[] = []; + for (var parent = node.parent; parent; parent = parent.parent) { + if (isExpression(parent) && isContextSensitiveExpression(parent)) { + containerNodes.unshift(parent); + } + } + + ts.forEach(containerNodes, node => { getTypeOfNode(node); }); + } + + function getSymbolAtLocation(node: Node): Symbol { + resolveLocation(node); + return getSymbolInfo(node); + } + + function getTypeAtLocation(node: Node): Type { + resolveLocation(node); + return getTypeOfNode(node); + } + + function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type { + resolveLocation(node); + // Get the narrowed type of symbol at given location instead of just getting + // the type of the symbol. + // eg. + // function foo(a: string | number) { + // if (typeof a === "string") { + // a/**/ + // } + // } + // getTypeOfSymbol for a would return type of parameter symbol string | number + // Unless we provide location /**/, checker wouldn't know how to narrow the type + // By using getNarrowedTypeOfSymbol would return string since it would be able to narrow + // it by typeguard in the if true condition + return getNarrowedTypeOfSymbol(symbol, node); + } + // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { var type = getTypeOfSymbol(symbol); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ab396d13a85..3aee8dd5430 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -903,8 +903,7 @@ module ts { getSymbolCount(): number; getTypeCount(): number; emitFiles(targetSourceFile?: SourceFile): EmitResult; - getParentOfSymbol(symbol: Symbol): Symbol; - getNarrowedTypeOfSymbol(symbol: Symbol, node: Node): Type; + getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; getPropertiesOfType(type: Type): Symbol[]; getPropertyOfType(type: Type, propertyName: string): Symbol; @@ -912,9 +911,9 @@ module ts { getIndexTypeOfType(type: Type, kind: IndexKind): Type; getReturnTypeOfSignature(signature: Signature): Type; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; - getSymbolInfo(node: Node): Symbol; + getSymbolAtLocation(node: Node): Symbol; getShorthandAssignmentValueSymbol(location: Node): Symbol; - getTypeOfNode(node: Node): Type; + getTypeAtLocation(node: Node): Type; typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; getSymbolDisplayBuilder(): SymbolDisplayBuilder; diff --git a/src/harness/typeWriter.ts b/src/harness/typeWriter.ts index 83aaaaf920a..11d2e62e10a 100644 --- a/src/harness/typeWriter.ts +++ b/src/harness/typeWriter.ts @@ -94,7 +94,7 @@ class TypeWriterWalker { } private getTypeOfNode(node: ts.Node): ts.Type { - var type = this.checker.getTypeOfNode(node); + var type = this.checker.getTypeOfLocation(node); ts.Debug.assert(type !== undefined, "type doesn't exist"); return type; } diff --git a/src/services/services.ts b/src/services/services.ts index 01e03a76232..d2775f3e183 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2433,7 +2433,7 @@ module ts { isMemberCompletion = true; if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) { - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // This is an alias, follow what it aliases if (symbol && symbol.flags & SymbolFlags.Import) { @@ -2450,7 +2450,7 @@ module ts { } } - var type = typeInfoResolver.getTypeOfNode(node); + var type = typeInfoResolver.getTypeAtLocation(node); if (type) { // Filter private properties forEach(type.getApparentProperties(), symbol => { @@ -2702,7 +2702,7 @@ module ts { // which is permissible given that it is backwards compatible; but really we should consider // passing the meaning for the node so that we don't report that a suggestion for a value is an interface. // We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration. - Debug.assert(session.typeChecker.getNarrowedTypeOfSymbol(symbol, location) !== undefined, "Could not find type for symbol"); + Debug.assert(session.typeChecker.getTypeOfSymbolAtLocation(symbol, location) !== undefined, "Could not find type for symbol"); var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), location, session.typeChecker, location, SemanticMeaning.All); return { name: entryName, @@ -2805,7 +2805,7 @@ module ts { if (!unionPropertyKind) { // If this was union of all methods, //make sure it has call signatures before we can label it as method - var typeOfUnionProperty = typeInfoResolver.getNarrowedTypeOfSymbol(symbol, location); + var typeOfUnionProperty = typeInfoResolver.getTypeOfSymbolAtLocation(symbol, location); if (typeOfUnionProperty.getCallSignatures().length) { return ScriptElementKind.memberFunctionElement; } @@ -2882,7 +2882,7 @@ module ts { symbolKind = ScriptElementKind.memberVariableElement; } - var type = typeResolver.getNarrowedTypeOfSymbol(symbol, location); + var type = typeResolver.getTypeOfSymbolAtLocation(symbol, location); if (type) { if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { var right = (location.parent).name; @@ -3094,7 +3094,7 @@ module ts { displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } else { - var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.moduleReference); + var internalAliasSymbol = typeResolver.getSymbolAtLocation(importDeclaration.moduleReference); if (internalAliasSymbol) { displayParts.push(spacePart()); displayParts.push(operatorPart(SyntaxKind.EqualsToken)); @@ -3204,7 +3204,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); if (!symbol) { // Try getting just type at this position and show switch (node.kind) { @@ -3214,7 +3214,7 @@ module ts { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: // For the identifiers/this/super etc get the type at position - var type = typeInfoResolver.getTypeOfNode(node); + var type = typeInfoResolver.getTypeAtLocation(node); if (type) { return { kind: ScriptElementKind.unknown, @@ -3331,7 +3331,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol @@ -3963,7 +3963,7 @@ module ts { return getReferencesForSuperKeyword(node); } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // Could not find a symbol e.g. unknown identifier if (!symbol) { @@ -4215,7 +4215,7 @@ module ts { return; } - var referenceSymbol = typeInfoResolver.getSymbolInfo(referenceLocation); + var referenceSymbol = typeInfoResolver.getSymbolAtLocation(referenceLocation); if (referenceSymbol) { var referenceSymbolDeclaration = referenceSymbol.valueDeclaration; var shorthandValueSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration); @@ -4457,7 +4457,7 @@ module ts { function getPropertySymbolFromTypeReference(typeReference: TypeReferenceNode) { if (typeReference) { - var type = typeInfoResolver.getTypeOfNode(typeReference); + var type = typeInfoResolver.getTypeAtLocation(typeReference); if (type) { var propertySymbol = typeInfoResolver.getPropertyOfType(type, propertyName); if (propertySymbol) { @@ -4981,7 +4981,7 @@ module ts { // Only walk into nodes that intersect the requested span. if (node && span.intersectsWith(node.getStart(), node.getWidth())) { if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) { - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); if (symbol) { var type = classifySymbol(symbol, getMeaningFromLocation(node)); if (type) { @@ -5397,7 +5397,7 @@ module ts { // Can only rename an identifier. if (node && node.kind === SyntaxKind.Identifier) { - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolAtLocation(node); // Only allow a symbol to be renamed if it actually has at least one declaration. if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index bdb76b77e8a..408fe60a6cd 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -457,7 +457,7 @@ module ts.SignatureHelp { var invocation = argumentListInfo.invocation; var callTarget = getInvokedExpression(invocation) - var callTargetSymbol = typeInfoResolver.getSymbolInfo(callTarget); + var callTargetSymbol = typeInfoResolver.getSymbolAtLocation(callTarget); var callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeInfoResolver, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); var items: SignatureHelpItem[] = map(candidates, candidateSignature => { var signatureHelpParameters: SignatureHelpParameter[]; diff --git a/tests/cases/fourslash/memberListOfVarInArrowExpression.ts b/tests/cases/fourslash/memberListOfVarInArrowExpression.ts new file mode 100644 index 00000000000..03da06989ae --- /dev/null +++ b/tests/cases/fourslash/memberListOfVarInArrowExpression.ts @@ -0,0 +1,18 @@ +/// + +////interface IMap { +//// [key: string]: T; +////} +////var map: IMap<{ a1: string; }[]>; +////var categories: string[]; +////each(categories, category => { +//// var changes = map[category]; +//// changes[0]./*1*/a1; +//// return each(changes, change => { +//// }); +////}); +////function each(items: T[], handler: (item: T) => void) { } + +goTo.marker('1'); +verify.quickInfoIs("(property) a1: string"); +verify.memberListContains("a1", "(property) a1: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts b/tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts new file mode 100644 index 00000000000..54287c76952 --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnVarInArrowExpression.ts @@ -0,0 +1,16 @@ +/// + +////interface IMap { +//// [key: string]: T; +////} +////var map: IMap; +////var categories: string[]; +////each(categories, category => { +//// var /*1*/changes = map[category]; +//// return each(changes, change => { +//// }); +////}); +////function each(items: T[], handler: (item: T) => void) { } + +goTo.marker('1'); +verify.quickInfoIs("(local var) changes: string[]", undefined); \ No newline at end of file