Skip to content

Commit 69d3ca7

Browse files
author
Andy
authored
When adding completions for a module, don't get the type of the module if not necessary. (#16768)
* When adding completions for a module, don't get the type of the module if not necessary. * Use SymbolFlags.Module alias
1 parent 6880ee3 commit 69d3ca7

File tree

5 files changed

+74
-36
lines changed

5 files changed

+74
-36
lines changed

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3604,6 +3604,10 @@ namespace ts {
36043604
return previous[previous.length - 1];
36053605
}
36063606

3607+
export function skipAlias(symbol: Symbol, checker: TypeChecker) {
3608+
return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
3609+
}
3610+
36073611
/** See comment on `declareModuleMember` in `binder.ts`. */
36083612
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
36093613
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;

src/services/codefixes/importFixes.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,7 @@ namespace ts.codefix {
222222
}
223223

224224
function getUniqueSymbolId(symbol: Symbol) {
225-
if (symbol.flags & SymbolFlags.Alias) {
226-
return getSymbolId(checker.getAliasedSymbol(symbol));
227-
}
228-
return getSymbolId(symbol);
225+
return getSymbolId(skipAlias(symbol, checker));
229226
}
230227

231228
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {

src/services/completions.ts

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -596,46 +596,48 @@ namespace ts.Completions {
596596
isNewIdentifierLocation = false;
597597

598598
// Since this is qualified name check its a type node location
599-
const isTypeLocation = isPartOfTypeNode(node.parent) || insideJsDocTagTypeExpression;
599+
const isTypeLocation = insideJsDocTagTypeExpression || isPartOfTypeNode(node.parent);
600600
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
601-
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) {
601+
if (isEntityName(node)) {
602602
let symbol = typeChecker.getSymbolAtLocation(node);
603+
if (symbol) {
604+
symbol = skipAlias(symbol, typeChecker);
605+
606+
if (symbol.flags & (SymbolFlags.Module | SymbolFlags.Enum)) {
607+
// Extract module or enum members
608+
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
609+
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName());
610+
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
611+
const isValidAccess = isRhsOfImportDeclaration ?
612+
// Any kind is allowed when dotting off namespace in internal import equals declaration
613+
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
614+
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
615+
for (const symbol of exportedSymbols) {
616+
if (isValidAccess(symbol)) {
617+
symbols.push(symbol);
618+
}
619+
}
603620

604-
// This is an alias, follow what it aliases
605-
if (symbol && symbol.flags & SymbolFlags.Alias) {
606-
symbol = typeChecker.getAliasedSymbol(symbol);
607-
}
608-
609-
if (symbol && symbol.flags & SymbolFlags.HasExports) {
610-
// Extract module or enum members
611-
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
612-
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName());
613-
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
614-
const isValidAccess = isRhsOfImportDeclaration ?
615-
// Any kind is allowed when dotting off namespace in internal import equals declaration
616-
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
617-
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
618-
forEach(exportedSymbols, symbol => {
619-
if (isValidAccess(symbol)) {
620-
symbols.push(symbol);
621+
// If the module is merged with a value, we must get the type of the class and add its propertes (for inherited static methods).
622+
if (!isTypeLocation && symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) {
623+
addTypeProperties(typeChecker.getTypeOfSymbolAtLocation(symbol, node));
621624
}
622-
});
625+
626+
return;
627+
}
623628
}
624629
}
625630

626631
if (!isTypeLocation) {
627-
const type = typeChecker.getTypeAtLocation(node);
628-
if (type) addTypeProperties(type);
632+
addTypeProperties(typeChecker.getTypeAtLocation(node));
629633
}
630634
}
631635

632636
function addTypeProperties(type: Type) {
633-
if (type) {
634-
// Filter private properties
635-
for (const symbol of type.getApparentProperties()) {
636-
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName())) {
637-
symbols.push(symbol);
638-
}
637+
// Filter private properties
638+
for (const symbol of type.getApparentProperties()) {
639+
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName())) {
640+
symbols.push(symbol);
639641
}
640642
}
641643

@@ -811,15 +813,13 @@ namespace ts.Completions {
811813
symbol = symbol.exportSymbol || symbol;
812814

813815
// This is an alias, follow what it aliases
814-
if (symbol && symbol.flags & SymbolFlags.Alias) {
815-
symbol = typeChecker.getAliasedSymbol(symbol);
816-
}
816+
symbol = skipAlias(symbol, typeChecker);
817817

818818
if (symbol.flags & SymbolFlags.Type) {
819819
return true;
820820
}
821821

822-
if (symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule)) {
822+
if (symbol.flags & SymbolFlags.Module) {
823823
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
824824
// If the exported symbols contains type,
825825
// symbol can be referenced at locations where type is allowed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////class C {
4+
//// static m() { }
5+
////}
6+
////
7+
////class D extends C {}
8+
////namespace D {
9+
//// export type T = number;
10+
////}
11+
////
12+
////let x: D./*type*/;
13+
////D./*value*/
14+
15+
goTo.marker("type");
16+
verify.completionListContains("T");
17+
verify.not.completionListContains("m");
18+
19+
goTo.marker("value");
20+
verify.not.completionListContains("T");
21+
verify.completionListContains("m");
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////namespace N {
4+
//// export type T = number;
5+
////}
6+
////const N = { m() {} };
7+
////let x: N./*type*/;
8+
////N./*value*/;
9+
10+
goTo.marker("type");
11+
verify.completionListContains("T");
12+
verify.not.completionListContains("m");
13+
14+
goTo.marker("value");
15+
verify.not.completionListContains("T");
16+
verify.completionListContains("m");

0 commit comments

Comments
 (0)