diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 28122240dcf..8885a7e5b25 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -85,7 +85,7 @@ module ts { checkProgram: checkProgram, emitFiles: invokeEmitter, getParentOfSymbol: getParentOfSymbol, - getTypeOfSymbol: getTypeOfSymbol, + getNarrowedTypeOfSymbol: getNarrowedTypeOfSymbol, getDeclaredTypeOfSymbol: getDeclaredTypeOfSymbol, getPropertiesOfType: getPropertiesOfType, getPropertyOfType: getPropertyOfType, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 973c06d6e2d..020b6432c7f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -701,7 +701,7 @@ module ts { checkProgram(): void; emitFiles(targetSourceFile?: SourceFile): EmitResult; getParentOfSymbol(symbol: Symbol): Symbol; - getTypeOfSymbol(symbol: Symbol): Type; + getNarrowedTypeOfSymbol(symbol: Symbol, node: Node): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; getPropertiesOfType(type: Type): Symbol[]; getPropertyOfType(type: Type, propertyName: string): Symbol; diff --git a/src/services/services.ts b/src/services/services.ts index 39199329cb7..eebf59f2fd9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2689,14 +2689,13 @@ module ts { var symbol = lookUp(activeCompletionSession.symbols, entryName); if (symbol) { - var type = session.typeChecker.getTypeOfSymbol(symbol); - Debug.assert(type !== undefined, "Could not find type for symbol"); var completionEntry = createCompletionEntry(symbol, session.typeChecker); // TODO(drosen): Right now we just permit *all* semantic meanings when calling 'getSymbolKind' // 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. var location = getTouchingPropertyName(sourceFile, position); + Debug.assert(session.typeChecker.getNarrowedTypeOfSymbol(symbol, location) !== undefined, "Could not find type for symbol"); var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), location, session.typeChecker, location, SemanticMeaning.All); return { name: entryName, @@ -2868,7 +2867,7 @@ module ts { symbolKind = ScriptElementKind.memberVariableElement; } - var type = typeResolver.getTypeOfSymbol(symbol); + var type = typeResolver.getNarrowedTypeOfSymbol(symbol, location); if (type) { if (location.parent && location.parent.kind === SyntaxKind.PropertyAccess) { var right = (location.parent).right; diff --git a/tests/cases/fourslash/completionEntryOnNarrowedType.ts b/tests/cases/fourslash/completionEntryOnNarrowedType.ts new file mode 100644 index 00000000000..714d3390e76 --- /dev/null +++ b/tests/cases/fourslash/completionEntryOnNarrowedType.ts @@ -0,0 +1,20 @@ +/// + +////function foo(strOrNum: string | number) { +//// /*1*/ +//// if (typeof strOrNum === "number") { +//// /*2*/ +//// } +//// else { +//// /*3*/ +//// } +////} + +goTo.marker('1'); +verify.completionListContains("strOrNum", "(parameter) strOrNum: string | number"); + +goTo.marker('2'); +verify.completionListContains("strOrNum", "(parameter) strOrNum: number"); + +goTo.marker('3'); +verify.completionListContains("strOrNum", "(parameter) strOrNum: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoOnNarrowedType.ts b/tests/cases/fourslash/quickInfoOnNarrowedType.ts new file mode 100644 index 00000000000..bc1d8126d76 --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnNarrowedType.ts @@ -0,0 +1,22 @@ +/// + +////function foo(strOrNum: string | number) { +//// if (typeof /*1*/strOrNum === "number") { +//// return /*2*/strOrNum; +//// } +//// else { +//// return /*3*/strOrNum.length; +//// } +////} + +goTo.marker('1'); +verify.quickInfoIs('(parameter) strOrNum: string | number'); +verify.completionListContains("strOrNum", "(parameter) strOrNum: string | number"); + +goTo.marker('2'); +verify.quickInfoIs('(parameter) strOrNum: number'); +verify.completionListContains("strOrNum", "(parameter) strOrNum: number"); + +goTo.marker('3'); +verify.quickInfoIs('(parameter) strOrNum: string'); +verify.completionListContains("strOrNum", "(parameter) strOrNum: string"); diff --git a/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts new file mode 100644 index 00000000000..79d794a9c0d --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts @@ -0,0 +1,51 @@ +/// + +////var strOrNum: string | number; +////module m { +//// var nonExportedStrOrNum: string | number; +//// export var exportedStrOrNum: string | number; +//// var num: number; +//// var str: string; +//// if (typeof /*1*/nonExportedStrOrNum === "number") { +//// num = /*2*/nonExportedStrOrNum; +//// } +//// else { +//// str = /*3*/nonExportedStrOrNum.length; +//// } +//// if (typeof /*4*/exportedStrOrNum === "number") { +//// strOrNum = /*5*/exportedStrOrNum; +//// } +//// else { +//// strOrNum = /*6*/exportedStrOrNum; +//// } +////} +////if (typeof m./*7*/exportedStrOrNum === "number") { +//// strOrNum = m./*8*/exportedStrOrNum; +////} +////else { +//// strOrNum = m./*9*/exportedStrOrNum; +////} + +goTo.marker('1'); +verify.quickInfoIs('(var) nonExportedStrOrNum: string | number'); +verify.completionListContains("nonExportedStrOrNum", "(var) nonExportedStrOrNum: string | number"); + +goTo.marker('2'); +verify.quickInfoIs('(var) nonExportedStrOrNum: number'); +verify.completionListContains("nonExportedStrOrNum", "(var) nonExportedStrOrNum: number"); + +goTo.marker('3'); +verify.quickInfoIs('(var) nonExportedStrOrNum: string'); +verify.completionListContains("nonExportedStrOrNum", "(var) nonExportedStrOrNum: string"); + +['4', '5', '6', '7', '8', '9'].forEach((marker, index, arr) => { + goTo.marker(marker); + verify.quickInfoIs('(var) m.exportedStrOrNum: string | number'); + verify.completionListContains("exportedStrOrNum", "(var) m.exportedStrOrNum: string | number"); +}); + +['7', '8', '9'].forEach((marker, index, arr) => { + goTo.marker(marker); + verify.quickInfoIs('(var) m.exportedStrOrNum: string | number'); + verify.memberListContains("exportedStrOrNum", "(var) m.exportedStrOrNum: string | number"); +}); \ No newline at end of file