diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cff964361a6..ae388c5b832 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1893,7 +1893,7 @@ namespace ts { function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags) { let targetSymbol = getTargetSymbol(symbol); - if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface) { + if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface || targetSymbol.flags & SymbolFlags.TypeAlias) { buildDisplayForTypeParametersAndDelimiters(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), writer, enclosingDeclaraiton, flags); } } diff --git a/src/services/services.ts b/src/services/services.ts index 8fda36d1160..92b62458f64 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3589,10 +3589,11 @@ namespace ts { containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { foo, | isFunction(containingNodeKind) || containingNodeKind === SyntaxKind.ClassDeclaration || // class AgetDeclarationOfKind(symbol, SyntaxKind.TypeParameter).parent; - let signature = typeChecker.getSignatureFromDeclaration(signatureDeclaration); - if (signatureDeclaration.kind === SyntaxKind.ConstructSignature) { - displayParts.push(keywordPart(SyntaxKind.NewKeyword)); + let container = getContainingFunction(location); + if (container) { + let signatureDeclaration = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter).parent; + let signature = typeChecker.getSignatureFromDeclaration(signatureDeclaration); + if (signatureDeclaration.kind === SyntaxKind.ConstructSignature) { + displayParts.push(keywordPart(SyntaxKind.NewKeyword)); + displayParts.push(spacePart()); + } + else if (signatureDeclaration.kind !== SyntaxKind.CallSignature && signatureDeclaration.name) { + addFullSymbolName(signatureDeclaration.symbol); + } + addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature)); + } + else { + // Type aliash type parameter + // For example + // type list = T[]; // Both T will go through same code path + let declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter).parent; + displayParts.push(keywordPart(SyntaxKind.TypeKeyword)); displayParts.push(spacePart()); + addFullSymbolName(declaration.symbol); + writeTypeParametersOfSymbol(declaration.symbol, sourceFile); } - else if (signatureDeclaration.kind !== SyntaxKind.CallSignature && signatureDeclaration.name) { - addFullSymbolName(signatureDeclaration.symbol); - } - addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature)); } } if (symbolFlags & SymbolFlags.EnumMember) { diff --git a/tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts b/tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts new file mode 100644 index 00000000000..f8afcdc418c --- /dev/null +++ b/tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts @@ -0,0 +1,18 @@ +/// + +////var C0 = class D {} +////var C3 = class D{} +////var C5 = class D{} + +goTo.marker("0"); +verify.completionListIsEmpty(); +goTo.marker("1"); +verify.completionListIsEmpty(); +goTo.marker("2"); +verify.completionListIsEmpty(); +goTo.marker("3"); +verify.completionListIsEmpty(); +goTo.marker("4"); +verify.not.completionListIsEmpty(); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInTypeParameterOfTypeAlias1.ts b/tests/cases/fourslash/completionListInTypeParameterOfTypeAlias1.ts new file mode 100644 index 00000000000..e6017eedbfc --- /dev/null +++ b/tests/cases/fourslash/completionListInTypeParameterOfTypeAlias1.ts @@ -0,0 +1,17 @@ +/// + +////type List1 = T[]; +////type List4 = /*2*/T[]; +////type List3 = /*3*/; + +goTo.marker("0"); +verify.completionListIsEmpty(); +goTo.marker("1"); +verify.not.completionListIsEmpty(); +goTo.marker("2"); +verify.completionListContains("T"); +goTo.marker("3"); +verify.not.completionListIsEmpty(); +verify.not.completionListContains("T"); +verify.completionListContains("T1"); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInTypeParameterOfTypeAlias2.ts b/tests/cases/fourslash/completionListInTypeParameterOfTypeAlias2.ts new file mode 100644 index 00000000000..80a910da798 --- /dev/null +++ b/tests/cases/fourslash/completionListInTypeParameterOfTypeAlias2.ts @@ -0,0 +1,19 @@ +/// + +////type Map1 = []; +////type Map1 = /*2*/[]; +////type Map1 = + +////type /*0*/List = /*2*/T[] +////type /*3*/List2 = /*5*/T[]; + +type List2 = T[]; + +type L = T[] +let typeAliashDisplayParts = [{ text: "type", kind: "keyword" }, { text: " ", kind: "space" }, { text: "List", kind: "aliasName" }, + { text: "<", kind: "punctuation" }, { text: "T", kind: "typeParameterName" }, { text: ">", kind: "punctuation" }]; + +let typeAliashDisplayParts2 = [{ text: "type", kind: "keyword" }, { text: " ", kind: "space" }, { text: "List2", kind: "aliasName" }, + { text: "<", kind: "punctuation" }, { text: "T", kind: "typeParameterName" }, { text: " ", kind: "space" }, { text: "extends", kind: "keyword" }, + { text: " ", kind: "space" }, { text: "string", kind: "keyword" }, { text: ">", kind: "punctuation" }]; + +let typeParameterDisplayParts = [{ text: "(", kind: "punctuation" }, { text: "type parameter", kind: "text" }, { text: ")", kind: "punctuation" }, { text: " ", kind: "space" }, + { text: "T", kind: "typeParameterName" }, { text: " ", kind: "space" }, { text: "in", kind: "keyword" }, { text: " ", kind: "space" } ] + + +goTo.marker('0'); +verify.verifyQuickInfoDisplayParts("type", "", { start: test.markerByName("0").position, length: "List".length }, + typeAliashDisplayParts.concat([{ text: " ", kind: "space" }, { text: "=", kind: "operator" }, { "text": " ", "kind": "space" }, { text: "T", kind: "typeParameterName" }, + { text: "[", kind: "punctuation" }, { text: "]", kind: "punctuation" }]), []); + +goTo.marker('1'); +verify.verifyQuickInfoDisplayParts("type parameter", "", { start: test.markerByName("1").position, length: "T".length }, + typeParameterDisplayParts.concat(typeAliashDisplayParts), []); + +goTo.marker('2'); +verify.verifyQuickInfoDisplayParts("type parameter", "", { start: test.markerByName("2").position, length: "T".length }, + typeParameterDisplayParts.concat(typeAliashDisplayParts), []); + +goTo.marker('3'); +verify.verifyQuickInfoDisplayParts("type", "", { start: test.markerByName("3").position, length: "List2".length }, + typeAliashDisplayParts2.concat([{ text: " ", kind: "space" }, { text: "=", kind: "operator" }, { "text": " ", "kind": "space" }, { text: "T", kind: "typeParameterName" }, + { text: "[", kind: "punctuation" }, { text: "]", kind: "punctuation" }]), []); + +goTo.marker('4'); +verify.verifyQuickInfoDisplayParts("type parameter", "", { start: test.markerByName("4").position, length: "T".length }, + typeParameterDisplayParts.concat(typeAliashDisplayParts2), []); + +goTo.marker('5'); +verify.verifyQuickInfoDisplayParts("type parameter", "", { start: test.markerByName("5").position, length: "T".length }, + typeParameterDisplayParts.concat(typeAliashDisplayParts2), []); \ No newline at end of file