Skip to content

Commit 8a88c1c

Browse files
authored
Don’t offer import fix for members of arrays or classes (#35635)
* Write failing test * Don’t offer import fix for members of arrays or classes
1 parent 543ec23 commit 8a88c1c

File tree

5 files changed

+66
-3
lines changed

5 files changed

+66
-3
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2878,7 +2878,11 @@ namespace ts {
28782878
}
28792879

28802880
const type = getTypeOfSymbol(exportEquals);
2881-
return type.flags & TypeFlags.Primitive ? undefined : getPropertyOfType(type, memberName);
2881+
return type.flags & TypeFlags.Primitive ||
2882+
getObjectFlags(type) & ObjectFlags.Class ||
2883+
isArrayOrTupleLikeType(type)
2884+
? undefined
2885+
: getPropertyOfType(type, memberName);
28822886
}
28832887

28842888
function getExportsOfSymbol(symbol: Symbol): SymbolTable {

src/harness/fourslashImpl.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,7 +2553,7 @@ namespace FourSlash {
25532553
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
25542554
* @param fileName Path to file where error should be retrieved from.
25552555
*/
2556-
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions): readonly ts.CodeFixAction[] {
2556+
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions, position?: number): readonly ts.CodeFixAction[] {
25572557
const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({
25582558
start: diagnostic.start,
25592559
length: diagnostic.length,
@@ -2564,7 +2564,12 @@ namespace FourSlash {
25642564
if (errorCode !== undefined && errorCode !== diagnostic.code) {
25652565
return;
25662566
}
2567-
2567+
if (position !== undefined && diagnostic.start !== undefined && diagnostic.length !== undefined) {
2568+
const span = ts.createTextRangeFromSpan({ start: diagnostic.start, length: diagnostic.length });
2569+
if (!ts.textRangeContainsPositionInclusive(span, position)) {
2570+
return;
2571+
}
2572+
}
25682573
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start!, diagnostic.start! + diagnostic.length!, [diagnostic.code], this.formatCodeSettings, preferences);
25692574
});
25702575
}
@@ -2614,6 +2619,23 @@ namespace FourSlash {
26142619
});
26152620
}
26162621

2622+
public verifyImportFixModuleSpecifiers(markerName: string, moduleSpecifiers: string[]) {
2623+
const marker = this.getMarkerByName(markerName);
2624+
const codeFixes = this.getCodeFixes(marker.fileName, ts.Diagnostics.Cannot_find_name_0.code, {
2625+
includeCompletionsForModuleExports: true,
2626+
includeCompletionsWithInsertText: true
2627+
}, marker.position).filter(f => f.fixId === ts.codefix.importFixId);
2628+
2629+
const actualModuleSpecifiers = ts.mapDefined(codeFixes, fix => {
2630+
return ts.forEach(ts.flatMap(fix.changes, c => c.textChanges), c => {
2631+
const match = /(?:from |require\()(['"])((?:(?!\1).)*)\1/.exec(c.newText);
2632+
return match?.[2];
2633+
});
2634+
});
2635+
2636+
assert.deepEqual(actualModuleSpecifiers, moduleSpecifiers);
2637+
}
2638+
26172639
public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) {
26182640
const name = "verifyDocCommentTemplate";
26192641
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;

src/harness/fourslashInterfaceImpl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,10 @@ namespace FourSlashInterface {
434434
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode, preferences);
435435
}
436436

437+
public importFixModuleSpecifiers(marker: string, moduleSpecifiers: string[]) {
438+
this.state.verifyImportFixModuleSpecifiers(marker, moduleSpecifiers);
439+
}
440+
437441
public navigationBar(json: any, options?: { checkSpans?: boolean }) {
438442
this.state.verifyNavigationBar(json, options);
439443
}

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ declare namespace FourSlashInterface {
330330
fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, formattingOptions?: FormatCodeOptions): void;
331331
getAndApplyCodeFix(errorCode?: number, index?: number): void;
332332
importFixAtPosition(expectedTextArray: string[], errorCode?: number, options?: UserPreferences): void;
333+
importFixModuleSpecifiers(marker: string, moduleSpecifiers: string[]): void;
333334

334335
navigationBar(json: any, options?: { checkSpans?: boolean }): void;
335336
navigationTree(json: any, options?: { checkSpans?: boolean }): void;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @target: es2015
4+
// @strict: true
5+
// @esModuleInterop: true
6+
7+
// @Filename: /array.ts
8+
////declare const arr: number[];
9+
////export = arr;
10+
11+
// @Filename: /class-instance-member.ts
12+
////class C { filter() {} }
13+
////export = new C();
14+
15+
// @Filename: /object-literal.ts
16+
////declare function filter(): void;
17+
////export = { filter };
18+
19+
// @Filename: /jquery.d.ts
20+
////interface JQueryStatic {
21+
//// filter(): void;
22+
////}
23+
////declare const $: JQueryStatic;
24+
////export = $;
25+
26+
// @Filename: /jquery.js
27+
////module.exports = {};
28+
29+
// @Filename: /index.ts
30+
////filter/**/
31+
32+
verify.importFixModuleSpecifiers('', ['./object-literal', './jquery']);

0 commit comments

Comments
 (0)