Skip to content

Commit b9c1e98

Browse files
authored
Fix completions of exports elsewhere in same file (#43755)
* Fix completions of exports elsewhere in same file * Undo messing up JSDoc-annotated module.exports assignments * Add other failing contextual type test * Rearrange contextual type logic for special assignments * Rename helper function
1 parent 514d8d8 commit b9c1e98

File tree

5 files changed

+100
-11
lines changed

5 files changed

+100
-11
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25080,12 +25080,11 @@ namespace ts {
2508025080
switch (kind) {
2508125081
case AssignmentDeclarationKind.None:
2508225082
return getTypeOfExpression(binaryExpression.left);
25083+
case AssignmentDeclarationKind.ThisProperty:
25084+
return getContextualTypeForThisPropertyAssignment(binaryExpression);
2508325085
case AssignmentDeclarationKind.Property:
25084-
case AssignmentDeclarationKind.ExportsProperty:
25085-
case AssignmentDeclarationKind.Prototype:
25086-
case AssignmentDeclarationKind.PrototypeProperty:
2508725086
if (isPossiblyAliasedThisProperty(binaryExpression, kind)) {
25088-
return getContextualTypeForThisPropertyAssignment(binaryExpression, kind);
25087+
return getContextualTypeForThisPropertyAssignment(binaryExpression);
2508925088
}
2509025089
// If `binaryExpression.left` was assigned a symbol, then this is a new declaration; otherwise it is an assignment to an existing declaration.
2509125090
// See `bindStaticPropertyAssignment` in `binder.ts`.
@@ -25118,9 +25117,15 @@ namespace ts {
2511825117
}
2511925118
return isInJSFile(decl) ? undefined : getTypeOfExpression(binaryExpression.left);
2512025119
}
25120+
case AssignmentDeclarationKind.ExportsProperty:
25121+
case AssignmentDeclarationKind.Prototype:
25122+
case AssignmentDeclarationKind.PrototypeProperty:
25123+
let valueDeclaration = binaryExpression.left.symbol?.valueDeclaration;
25124+
// falls through
2512125125
case AssignmentDeclarationKind.ModuleExports:
25122-
case AssignmentDeclarationKind.ThisProperty:
25123-
return getContextualTypeForThisPropertyAssignment(binaryExpression, kind);
25126+
valueDeclaration ||= binaryExpression.symbol?.valueDeclaration;
25127+
const annotated = valueDeclaration && getEffectiveTypeAnnotationNode(valueDeclaration);
25128+
return annotated ? getTypeFromTypeNode(annotated) : undefined;
2512425129
case AssignmentDeclarationKind.ObjectDefinePropertyValue:
2512525130
case AssignmentDeclarationKind.ObjectDefinePropertyExports:
2512625131
case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
@@ -25142,7 +25147,7 @@ namespace ts {
2514225147
return isThisInitializedDeclaration(symbol?.valueDeclaration);
2514325148
}
2514425149

25145-
function getContextualTypeForThisPropertyAssignment(binaryExpression: BinaryExpression, kind: AssignmentDeclarationKind): Type | undefined {
25150+
function getContextualTypeForThisPropertyAssignment(binaryExpression: BinaryExpression): Type | undefined {
2514625151
if (!binaryExpression.symbol) return getTypeOfExpression(binaryExpression.left);
2514725152
if (binaryExpression.symbol.valueDeclaration) {
2514825153
const annotated = getEffectiveTypeAnnotationNode(binaryExpression.symbol.valueDeclaration);
@@ -25153,7 +25158,6 @@ namespace ts {
2515325158
}
2515425159
}
2515525160
}
25156-
if (kind === AssignmentDeclarationKind.ModuleExports) return undefined;
2515725161
const thisAccess = cast(binaryExpression.left, isAccessExpression);
2515825162
if (!isObjectLiteralMethod(getThisContainer(thisAccess.expression, /*includeArrowFunctions*/ false))) {
2515925163
return undefined;
@@ -38874,10 +38878,10 @@ namespace ts {
3887438878

3887538879
switch (location.kind) {
3887638880
case SyntaxKind.SourceFile:
38877-
if (!isExternalOrCommonJsModule(<SourceFile>location)) break;
38881+
if (!isExternalModule(<SourceFile>location)) break;
3887838882
// falls through
3887938883
case SyntaxKind.ModuleDeclaration:
38880-
copySymbols(getSymbolOfNode(location as ModuleDeclaration | SourceFile).exports!, meaning & SymbolFlags.ModuleMember);
38884+
copyLocallyVisibleExportSymbols(getSymbolOfNode(location as ModuleDeclaration | SourceFile).exports!, meaning & SymbolFlags.ModuleMember);
3888138885
break;
3888238886
case SyntaxKind.EnumDeclaration:
3888338887
copySymbols(getSymbolOfNode(location as EnumDeclaration).exports!, meaning & SymbolFlags.EnumMember);
@@ -38946,6 +38950,17 @@ namespace ts {
3894638950
});
3894738951
}
3894838952
}
38953+
38954+
function copyLocallyVisibleExportSymbols(source: SymbolTable, meaning: SymbolFlags): void {
38955+
if (meaning) {
38956+
source.forEach(symbol => {
38957+
// Similar condition as in `resolveNameHelper`
38958+
if (!getDeclarationOfKind(symbol, SyntaxKind.ExportSpecifier) && !getDeclarationOfKind(symbol, SyntaxKind.NamespaceExport)) {
38959+
copySymbol(symbol, meaning);
38960+
}
38961+
});
38962+
}
38963+
}
3894938964
}
3895038965

3895138966
function isTypeDeclarationName(name: Node): boolean {

src/services/completions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2992,7 +2992,8 @@ namespace ts.Completions {
29922992
if (type) {
29932993
return type;
29942994
}
2995-
if (isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
2995+
if (isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken && node === node.parent.left) {
2996+
// Object literal is assignment pattern: ({ | } = x)
29962997
return typeChecker.getTypeAtLocation(node.parent);
29972998
}
29982999
return undefined;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @Filename: other.ts
4+
//// export {};
5+
6+
// @Filename: index.ts
7+
//// const c = 0;
8+
//// export { c as yeahThisIsTotallyInScopeHuh };
9+
//// export * as alsoNotInScope from "./other";
10+
////
11+
//// /**/
12+
13+
verify.completions({
14+
marker: "",
15+
includes: "c",
16+
excludes: ["yeahThisIsTotallyInScopeHuh", "alsoNotInScope"],
17+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @allowJs: true
4+
// @checkJs: true
5+
6+
// @Filename: index.js
7+
//// const almanac = 0;
8+
//// module.exports = {
9+
//// a/**/
10+
//// };
11+
12+
verify.completions({
13+
marker: "",
14+
isNewIdentifierLocation: true,
15+
includes: "almanac",
16+
excludes: "a",
17+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @checkJs: true
4+
// @allowJs: true
5+
6+
// @Filename: index.js
7+
//// module.exports = {
8+
//// a/*1*/
9+
//// }
10+
////
11+
//// exports.foo = {
12+
//// a/*2*/
13+
//// }
14+
////
15+
//// function F() {
16+
//// this.blah = {
17+
//// a/*3*/
18+
//// };
19+
//// }
20+
////
21+
//// F.foo = {
22+
//// a/*4*/
23+
//// }
24+
////
25+
//// F.prototype = {
26+
//// a/*5*/
27+
//// }
28+
////
29+
//// F.prototype.x = {
30+
//// a/*6*/
31+
//// }
32+
33+
[1, 2, 3, 4, 5, 6].forEach(marker => {
34+
verify.completions({
35+
marker: `${marker}`,
36+
excludes: "a",
37+
isNewIdentifierLocation: true,
38+
});
39+
});

0 commit comments

Comments
 (0)