Skip to content

Commit 672b0e3

Browse files
author
Andy
authored
Have flatMap return a ReadonlyArray by default (#28205)
1 parent c97fc64 commit 672b0e3

19 files changed

+80
-57
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28090,7 +28090,7 @@ namespace ts {
2809028090
return ts.typeHasCallOrConstructSignatures(type, checker);
2809128091
}
2809228092

28093-
function getRootSymbols(symbol: Symbol): Symbol[] {
28093+
function getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol> {
2809428094
const roots = getImmediateRootSymbols(symbol);
2809528095
return roots ? flatMap(roots, getRootSymbols) : [symbol];
2809628096
}

src/compiler/core.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,27 @@ namespace ts {
511511
* @param array The array to map.
512512
* @param mapfn The callback used to map the result into one or more values.
513513
*/
514-
export function flatMap<T, U>(array: ReadonlyArray<T>, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[];
515-
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] | undefined;
516-
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] | undefined {
514+
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): ReadonlyArray<U> {
517515
let result: U[] | undefined;
518516
if (array) {
519-
result = [];
517+
for (let i = 0; i < array.length; i++) {
518+
const v = mapfn(array[i], i);
519+
if (v) {
520+
if (isArray(v)) {
521+
result = addRange(result, v);
522+
}
523+
else {
524+
result = append(result, v);
525+
}
526+
}
527+
}
528+
}
529+
return result || emptyArray;
530+
}
531+
532+
export function flatMapToMutable<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] {
533+
const result: U[] = [];
534+
if (array) {
520535
for (let i = 0; i < array.length; i++) {
521536
const v = mapfn(array[i], i);
522537
if (v) {

src/compiler/transformers/declarations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ namespace ts {
10431043
const modifiers = createNodeArray(ensureModifiers(input, isPrivate));
10441044
const typeParameters = ensureTypeParams(input, input.typeParameters);
10451045
const ctor = getFirstConstructorWithBody(input);
1046-
let parameterProperties: PropertyDeclaration[] | undefined;
1046+
let parameterProperties: ReadonlyArray<PropertyDeclaration> | undefined;
10471047
if (ctor) {
10481048
const oldDiag = getSymbolAccessibilityDiagnostic;
10491049
parameterProperties = compact(flatMap(ctor.parameters, param => {

src/compiler/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2793,7 +2793,7 @@ namespace ts {
27932793
export interface ParseConfigHost {
27942794
useCaseSensitiveFileNames: boolean;
27952795

2796-
readDirectory(rootDir: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string> | undefined, includes: ReadonlyArray<string>, depth?: number): string[];
2796+
readDirectory(rootDir: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string> | undefined, includes: ReadonlyArray<string>, depth?: number): ReadonlyArray<string>;
27972797

27982798
/**
27992799
* Gets a value indicating whether the specified path exists and is a file.
@@ -3079,7 +3079,7 @@ namespace ts {
30793079

30803080
getFullyQualifiedName(symbol: Symbol): string;
30813081
getAugmentedPropertiesOfType(type: Type): Symbol[];
3082-
getRootSymbols(symbol: Symbol): Symbol[];
3082+
getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol>;
30833083
getContextualType(node: Expression): Type | undefined;
30843084
/* @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined;
30853085
/* @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined;

src/compiler/utilities.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3038,7 +3038,7 @@ namespace ts {
30383038
return fileDiagnostics.get(fileName) || [];
30393039
}
30403040

3041-
const fileDiags: Diagnostic[] = flatMap(filesWithDiagnostics, f => fileDiagnostics.get(f));
3041+
const fileDiags: Diagnostic[] = flatMapToMutable(filesWithDiagnostics, f => fileDiagnostics.get(f));
30423042
if (!nonFileDiagnostics.length) {
30433043
return fileDiags;
30443044
}
@@ -5234,7 +5234,7 @@ namespace ts {
52345234
}
52355235
if (isJSDocTypeAlias(node)) {
52365236
Debug.assert(node.parent.kind === SyntaxKind.JSDocComment);
5237-
return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined) as ReadonlyArray<TypeParameterDeclaration>;
5237+
return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined);
52385238
}
52395239
if (node.typeParameters) {
52405240
return node.typeParameters;
@@ -7777,7 +7777,7 @@ namespace ts {
77777777
return `^(${pattern})${terminator}`;
77787778
}
77797779

7780-
export function getRegularExpressionsForWildcards(specs: ReadonlyArray<string> | undefined, basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined {
7780+
export function getRegularExpressionsForWildcards(specs: ReadonlyArray<string> | undefined, basePath: string, usage: "files" | "directories" | "exclude"): ReadonlyArray<string> | undefined {
77817781
if (specs === undefined || specs.length === 0) {
77827782
return undefined;
77837783
}

src/harness/fourslash.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ namespace FourSlash {
491491
];
492492
}
493493

494-
private getAllDiagnostics(): ts.Diagnostic[] {
494+
private getAllDiagnostics(): ReadonlyArray<ts.Diagnostic> {
495495
return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName => {
496496
if (!ts.isAnySupportedFileExtension(fileName)) {
497497
return [];
@@ -532,7 +532,7 @@ namespace FourSlash {
532532
predicate(start!, start! + length!, startMarker.position, endMarker === undefined ? undefined : endMarker.position)); // TODO: GH#18217
533533
}
534534

535-
private printErrorLog(expectErrors: boolean, errors: ts.Diagnostic[]) {
535+
private printErrorLog(expectErrors: boolean, errors: ReadonlyArray<ts.Diagnostic>): void {
536536
if (expectErrors) {
537537
Harness.IO.log("Expected error not found. Error list is:");
538538
}
@@ -613,7 +613,7 @@ namespace FourSlash {
613613
this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinitionAndBoundSpan());
614614
}
615615

616-
private getGoToDefinition(): ts.DefinitionInfo[] {
616+
private getGoToDefinition(): ReadonlyArray<ts.DefinitionInfo> {
617617
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
618618
}
619619

@@ -626,7 +626,7 @@ namespace FourSlash {
626626
this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition));
627627
}
628628

629-
private verifyGoToX(arg0: any, endMarkerNames: ArrayOrSingle<string> | undefined, getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) {
629+
private verifyGoToX(arg0: any, endMarkerNames: ArrayOrSingle<string> | undefined, getDefs: () => ReadonlyArray<ts.DefinitionInfo> | ts.DefinitionInfoAndBoundSpan | undefined) {
630630
if (endMarkerNames) {
631631
this.verifyGoToXPlain(arg0, endMarkerNames, getDefs);
632632
}
@@ -646,7 +646,7 @@ namespace FourSlash {
646646
}
647647
}
648648

649-
private verifyGoToXPlain(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>, getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) {
649+
private verifyGoToXPlain(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>, getDefs: () => ReadonlyArray<ts.DefinitionInfo> | ts.DefinitionInfoAndBoundSpan | undefined) {
650650
for (const start of toArray(startMarkerNames)) {
651651
this.verifyGoToXSingle(start, endMarkerNames, getDefs);
652652
}
@@ -1890,7 +1890,7 @@ Actual: ${stringify(fullActual)}`);
18901890

18911891
public verifyRangesInImplementationList(markerName: string) {
18921892
this.goToMarker(markerName);
1893-
const implementations: ImplementationLocationInformation[] = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
1893+
const implementations: ReadonlyArray<ImplementationLocationInformation> = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
18941894
if (!implementations || !implementations.length) {
18951895
this.raiseError("verifyRangesInImplementationList failed - expected to find at least one implementation location but got 0");
18961896
}
@@ -2383,7 +2383,7 @@ Actual: ${stringify(fullActual)}`);
23832383
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
23842384
* @param fileName Path to file where error should be retrieved from.
23852385
*/
2386-
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions): ts.CodeFixAction[] {
2386+
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions): ReadonlyArray<ts.CodeFixAction> {
23872387
const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({
23882388
start: diagnostic.start,
23892389
length: diagnostic.length,

src/harness/harness.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ namespace Harness {
490490
getExecutingFilePath(): string;
491491
getWorkspaceRoot(): string;
492492
exit(exitCode?: number): void;
493-
readDirectory(path: string, extension?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
493+
readDirectory(path: string, extension?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): ReadonlyArray<string>;
494494
getAccessibleFileSystemEntries(dirname: string): ts.FileSystemEntries;
495495
tryEnableSourceMapsForHost?(): void;
496496
getEnvironmentVariable?(name: string): string;

src/server/project.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ namespace ts.server {
548548
}
549549

550550
getExternalFiles(): SortedReadonlyArray<string> {
551-
return toSortedArray(flatMap(this.plugins, plugin => {
551+
return toSortedArray(flatMapToMutable(this.plugins, plugin => {
552552
if (typeof plugin.getExternalFiles !== "function") return;
553553
try {
554554
return plugin.getExternalFiles(this);

src/server/protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,7 @@ namespace ts.server.protocol {
10231023
/**
10241024
* The file locations referencing the symbol.
10251025
*/
1026-
refs: ReferencesResponseItem[];
1026+
refs: ReadonlyArray<ReferencesResponseItem>;
10271027

10281028
/**
10291029
* The name of the symbol.

src/server/session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ namespace ts.server {
267267
projects: Projects,
268268
action: (project: Project, value: T) => ReadonlyArray<U> | U | undefined,
269269
): U[] {
270-
const outputs = flatMap(isArray(projects) ? projects : projects.projects, project => action(project, defaultValue));
270+
const outputs = flatMapToMutable(isArray(projects) ? projects : projects.projects, project => action(project, defaultValue));
271271
if (!isArray(projects) && projects.symLinkedProjects) {
272272
projects.symLinkedProjects.forEach((projects, path) => {
273273
const value = getValue(path as Path);
@@ -1230,7 +1230,7 @@ namespace ts.server {
12301230
const nameSpan = nameInfo && nameInfo.textSpan;
12311231
const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0;
12321232
const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : "";
1233-
const refs: protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol =>
1233+
const refs: ReadonlyArray<protocol.ReferencesResponseItem> = flatMap(references, referencedSymbol =>
12341234
referencedSymbol.references.map(({ fileName, textSpan, isWriteAccess, isDefinition }): protocol.ReferencesResponseItem => {
12351235
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(fileName));
12361236
const start = scriptInfo.positionToLineOffset(textSpan.start);

src/services/codeFixProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ namespace ts {
6262
return arrayFrom(errorCodeToFixes.keys());
6363
}
6464

65-
export function getFixes(context: CodeFixContext): CodeFixAction[] {
65+
export function getFixes(context: CodeFixContext): ReadonlyArray<CodeFixAction> {
6666
return flatMap(errorCodeToFixes.get(String(context.errorCode)) || emptyArray, f => f.getCodeActions(context));
6767
}
6868

src/services/codefixes/importFixes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ namespace ts.codefix {
289289
// `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
290290
exportedSymbolIsTypeOnly && isJs ? { kind: ImportFixKind.ImportType, moduleSpecifier, position: Debug.assertDefined(position) } : { kind: ImportFixKind.AddNew, moduleSpecifier, importKind }));
291291
// Sort to keep the shortest paths first
292-
return choicesForEachExportingModule.sort((a, b) => a.moduleSpecifier.length - b.moduleSpecifier.length);
292+
return sort(choicesForEachExportingModule, (a, b) => a.moduleSpecifier.length - b.moduleSpecifier.length);
293293
}
294294

295295
function getFixesForAddImport(

src/services/codefixes/inferFromUsage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ namespace ts.codefix {
228228

229229
function addJSDocTags(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parent: HasJSDoc, newTags: ReadonlyArray<JSDocTag>): void {
230230
const comments = mapDefined(parent.jsDoc, j => j.comment);
231-
const oldTags = flatMap(parent.jsDoc, j => j.tags);
231+
const oldTags = flatMapToMutable(parent.jsDoc, j => j.tags);
232232
const unmergedNewTags = newTags.filter(newTag => !oldTags || !oldTags.some((tag, i) => {
233233
const merged = tryMergeJsdocTags(tag, newTag);
234234
if (merged) oldTags[i] = merged;

src/services/findAllReferences.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/* @internal */
22
namespace ts.FindAllReferences {
33
export interface SymbolAndEntries {
4-
definition: Definition | undefined;
5-
references: Entry[];
4+
readonly definition: Definition | undefined;
5+
readonly references: ReadonlyArray<Entry>;
66
}
77

88
export const enum DefinitionKind { Symbol, Label, Keyword, This, String }
@@ -60,7 +60,7 @@ namespace ts.FindAllReferences {
6060
return map(referenceEntries, entry => toImplementationLocation(entry, checker));
6161
}
6262

63-
function getImplementationReferenceEntries(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, node: Node, position: number): Entry[] | undefined {
63+
function getImplementationReferenceEntries(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, node: Node, position: number): ReadonlyArray<Entry> | undefined {
6464
if (node.kind === SyntaxKind.SourceFile) {
6565
return undefined;
6666
}
@@ -94,11 +94,19 @@ namespace ts.FindAllReferences {
9494

9595
export type ToReferenceOrRenameEntry<T> = (entry: Entry, originalNode: Node) => T;
9696

97-
export function getReferenceEntriesForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap<true> = arrayToSet(sourceFiles, f => f.fileName)): Entry[] | undefined {
97+
export function getReferenceEntriesForNode(
98+
position: number,
99+
node: Node,
100+
program: Program,
101+
sourceFiles: ReadonlyArray<SourceFile>,
102+
cancellationToken: CancellationToken,
103+
options: Options = {},
104+
sourceFilesSet: ReadonlyMap<true> = arrayToSet(sourceFiles, f => f.fileName),
105+
): ReadonlyArray<Entry> | undefined {
98106
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options, sourceFilesSet));
99107
}
100108

101-
function flattenEntries(referenceSymbols: SymbolAndEntries[] | undefined): Entry[] | undefined {
109+
function flattenEntries(referenceSymbols: SymbolAndEntries[] | undefined): ReadonlyArray<Entry> | undefined {
102110
return referenceSymbols && flatMap(referenceSymbols, r => r.references);
103111
}
104112

src/services/goToDefinition.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* @internal */
22
namespace ts.GoToDefinition {
3-
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] | undefined {
3+
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): ReadonlyArray<DefinitionInfo> | undefined {
44
const reference = getReferenceAtPosition(sourceFile, position, program);
55
if (reference) {
66
return [getDefinitionInfoForFileReference(reference.fileName, reference.file.fileName)];
@@ -129,7 +129,7 @@ namespace ts.GoToDefinition {
129129
}
130130

131131
/// Goto type
132-
export function getTypeDefinitionAtPosition(typeChecker: TypeChecker, sourceFile: SourceFile, position: number): DefinitionInfo[] | undefined {
132+
export function getTypeDefinitionAtPosition(typeChecker: TypeChecker, sourceFile: SourceFile, position: number): ReadonlyArray<DefinitionInfo> | undefined {
133133
const node = getTouchingPropertyName(sourceFile, position);
134134
if (node === sourceFile) {
135135
return undefined;
@@ -145,7 +145,7 @@ namespace ts.GoToDefinition {
145145
return fromReturnType && fromReturnType.length !== 0 ? fromReturnType : definitionFromType(typeAtLocation, typeChecker, node);
146146
}
147147

148-
function definitionFromType(type: Type, checker: TypeChecker, node: Node): DefinitionInfo[] {
148+
function definitionFromType(type: Type, checker: TypeChecker, node: Node): ReadonlyArray<DefinitionInfo> {
149149
return flatMap(type.isUnion() && !(type.flags & TypeFlags.Enum) ? type.types : [type], t =>
150150
t.symbol && getDefinitionFromSymbol(checker, t.symbol, node));
151151
}

src/services/services.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,7 @@ namespace ts {
14961496
}
14971497

14981498
/// Goto definition
1499-
function getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] | undefined {
1499+
function getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined {
15001500
synchronizeHostData();
15011501
return GoToDefinition.getDefinitionAtPosition(program, getValidSourceFile(fileName), position);
15021502
}
@@ -1506,7 +1506,7 @@ namespace ts {
15061506
return GoToDefinition.getDefinitionAndBoundSpan(program, getValidSourceFile(fileName), position);
15071507
}
15081508

1509-
function getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] | undefined {
1509+
function getTypeDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined {
15101510
synchronizeHostData();
15111511
return GoToDefinition.getTypeDefinitionAtPosition(program.getTypeChecker(), getValidSourceFile(fileName), position);
15121512
}
@@ -1519,7 +1519,7 @@ namespace ts {
15191519
}
15201520

15211521
/// References and Occurrences
1522-
function getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined {
1522+
function getOccurrencesAtPosition(fileName: string, position: number): ReadonlyArray<ReferenceEntry> | undefined {
15231523
return flatMap(getDocumentHighlights(fileName, position, [fileName]), entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
15241524
fileName: entry.fileName,
15251525
textSpan: highlightSpan.textSpan,

0 commit comments

Comments
 (0)