diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e52b8f5d36055..af7dc2b89ded8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17,10 +17,10 @@ import { appendIfUnique, ArrayBindingPattern, arrayFrom, + arrayIsEqualTo, arrayIsHomogeneous, ArrayLiteralExpression, arrayOf, - arraysEqual, arrayToMultiMap, ArrayTypeNode, ArrowFunction, @@ -25860,7 +25860,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function inferTypesFromTemplateLiteralType(source: Type, target: TemplateLiteralType): Type[] | undefined { return source.flags & TypeFlags.StringLiteral ? inferFromLiteralPartsToTemplateLiteral([(source as StringLiteralType).value], emptyArray, target) : source.flags & TypeFlags.TemplateLiteral ? - arraysEqual((source as TemplateLiteralType).texts, target.texts) ? map((source as TemplateLiteralType).types, (s, i) => { + arrayIsEqualTo((source as TemplateLiteralType).texts, target.texts) ? map((source as TemplateLiteralType).types, (s, i) => { return isTypeAssignableTo(getBaseConstraintOrType(s), getBaseConstraintOrType(target.types[i])) ? s : getStringLikeTypeForType(s); }) : inferFromLiteralPartsToTemplateLiteral((source as TemplateLiteralType).texts, (source as TemplateLiteralType).types, target) : @@ -28625,7 +28625,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))); } const result = recombineUnknownType(getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction)); - if (result !== declaredType && result.flags & declaredType.flags & TypeFlags.Union && arraysEqual((result as UnionType).types, (declaredType as UnionType).types)) { + if (result !== declaredType && result.flags & declaredType.flags & TypeFlags.Union && arrayIsEqualTo((result as UnionType).types, (declaredType as UnionType).types)) { return declaredType; } return result; diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 4a9918c9671e9..7698dcdbd10a3 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -2580,9 +2580,7 @@ export function convertToTSConfig(configParseResult: ParsedCommandLine, configFi /** @internal */ export function optionMapToObject(optionMap: Map): object { - return { - ...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}), - }; + return Object.fromEntries(optionMap); } function filterSameAsDefaultInclude(specs: readonly string[] | undefined) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index d7f6fe0580d5f..977da801ed946 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -11,6 +11,8 @@ import { TextSpan, } from "./_namespaces/ts.js"; +/* eslint-disable @typescript-eslint/prefer-for-of */ + /** @internal */ export const emptyArray: never[] = [] as never[]; /** @internal */ @@ -20,7 +22,7 @@ export const emptySet: ReadonlySet = new Set(); /** @internal */ export function length(array: readonly any[] | undefined): number { - return array ? array.length : 0; + return array !== undefined ? array.length : 0; } /** @@ -31,7 +33,7 @@ export function length(array: readonly any[] | undefined): number { * @internal */ export function forEach(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const result = callback(array[i], i); if (result) { @@ -48,7 +50,7 @@ export function forEach(array: readonly T[] | undefined, callback: (elemen * @internal */ export function forEachRight(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { - if (array) { + if (array !== undefined) { for (let i = array.length - 1; i >= 0; i--) { const result = callback(array[i], i); if (result) { @@ -124,7 +126,7 @@ export function intersperse(input: T[], element: T): T[] { } const result: T[] = []; for (let i = 0, n = input.length; i < n; i++) { - if (i) result.push(element); + if (i !== 0) result.push(element); result.push(input[i]); } return result; @@ -143,7 +145,7 @@ export function every(array: readonly T[] | undefined, callback: /** @internal */ export function every(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean; export function every(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean { - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { if (!callback(array[i], i)) { return false; @@ -234,9 +236,9 @@ export function findMap(array: readonly T[], callback: (element: T, index: /** @internal */ export function contains(array: readonly T[] | undefined, value: T, equalityComparer: EqualityComparer = equateValues): boolean { - if (array) { - for (const v of array) { - if (equalityComparer(v, value)) { + if (array !== undefined) { + for (let i = 0; i < array.length; i++) { + if (equalityComparer(array[i], value)) { return true; } } @@ -244,14 +246,9 @@ export function contains(array: readonly T[] | undefined, value: T, equalityC return false; } -/** @internal */ -export function arraysEqual(a: readonly T[], b: readonly T[], equalityComparer: EqualityComparer = equateValues): boolean { - return a.length === b.length && a.every((x, i) => equalityComparer(x, b[i])); -} - /** @internal */ export function indexOfAnyCharCode(text: string, charCodes: readonly number[], start?: number): number { - for (let i = start || 0; i < text.length; i++) { + for (let i = start ?? 0; i < text.length; i++) { if (contains(charCodes, text.charCodeAt(i))) { return i; } @@ -262,7 +259,7 @@ export function indexOfAnyCharCode(text: string, charCodes: readonly number[], s /** @internal */ export function countWhere(array: readonly T[] | undefined, predicate: (x: T, i: number) => boolean): number { let count = 0; - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const v = array[i]; if (predicate(v, i)) { @@ -296,7 +293,7 @@ export function filter(array: readonly T[] | undefined, f: (x: T export function filter(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined; /** @internal */ export function filter(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined { - if (array) { + if (array !== undefined) { const len = array.length; let i = 0; while (i < len && f(array[i])) i++; @@ -340,7 +337,7 @@ export function map(array: readonly T[] | undefined, f: (x: T, i: number) /** @internal */ export function map(array: readonly T[] | undefined, f: (x: T, i: number) => U): U[] | undefined { let result: U[] | undefined; - if (array) { + if (array !== undefined) { result = []; for (let i = 0; i < array.length; i++) { result.push(f(array[i], i)); @@ -369,7 +366,7 @@ export function sameMap(array: T[] | undefined, f: (x: T, i: number) = export function sameMap(array: readonly T[] | undefined, f: (x: T, i: number) => U): readonly U[] | undefined; /** @internal */ export function sameMap(array: readonly T[] | undefined, f: (x: T, i: number) => U): readonly U[] | undefined { - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const item = array[i]; const mapped = f(item, i); @@ -395,7 +392,8 @@ export function sameMap(array: readonly T[] | undefined, f: (x: T, i: */ export function flatten(array: T[][] | readonly (T | readonly T[] | undefined)[]): T[] { const result = []; - for (const v of array) { + for (let i = 0; i < array.length; i++) { + const v = array[i]; if (v) { if (isArray(v)) { addRange(result, v); @@ -418,7 +416,7 @@ export function flatten(array: T[][] | readonly (T | readonly T[] | undefined */ export function flatMap(array: readonly T[] | undefined, mapfn: (x: T, i: number) => U | readonly U[] | undefined): readonly U[] { let result: U[] | undefined; - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const v = mapfn(array[i], i); if (v) { @@ -431,13 +429,13 @@ export function flatMap(array: readonly T[] | undefined, mapfn: (x: T, i: } } } - return result || emptyArray; + return result ?? emptyArray; } /** @internal */ export function flatMapToMutable(array: readonly T[] | undefined, mapfn: (x: T, i: number) => U | readonly U[] | undefined): U[] { const result: U[] = []; - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const v = mapfn(array[i], i); if (v) { @@ -477,7 +475,7 @@ export function sameFlatMap(array: readonly T[], mapfn: (x: T, i: number) => /** @internal */ export function sameFlatMap(array: readonly T[], mapfn: (x: T, i: number) => T | readonly T[]): readonly T[] { let result: T[] | undefined; - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const item = array[i]; const mapped = mapfn(item, i); @@ -494,7 +492,7 @@ export function sameFlatMap(array: readonly T[], mapfn: (x: T, i: number) => } } } - return result || array; + return result ?? array; } /** @internal */ @@ -513,7 +511,7 @@ export function mapAllOrFail(array: readonly T[], mapFn: (x: T, i: number) /** @internal */ export function mapDefined(array: readonly T[] | undefined, mapFn: (x: T, i: number) => U | undefined): U[] { const result: U[] = []; - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const mapped = mapFn(array[i], i); if (mapped !== undefined) { @@ -534,30 +532,6 @@ export function* mapDefinedIterator(iter: Iterable, mapFn: (x: T) => U } } -/** @internal */ -export function mapDefinedEntries(map: ReadonlyMap, f: (key: K1, value: V1) => readonly [K2, V2] | undefined): Map; -/** @internal */ -export function mapDefinedEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): Map | undefined; -/** @internal */ -export function mapDefinedEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): Map | undefined { - if (!map) { - return undefined; - } - - const result = new Map(); - map.forEach((value, key) => { - const entry = f(key, value); - if (entry !== undefined) { - const [newKey, newValue] = entry; - if (newKey !== undefined && newValue !== undefined) { - result.set(newKey, newValue); - } - } - }); - - return result; -} - /** @internal */ export function getOrUpdate(map: Map, key: K, callback: () => V) { if (map.has(key)) { @@ -597,7 +571,7 @@ export function spanMap(array: readonly T[] | undefined, keyfn: (x: T, /** @internal */ export function spanMap(array: readonly T[] | undefined, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] | undefined { let result: U[] | undefined; - if (array) { + if (array !== undefined) { result = []; const len = array.length; let previousKey: K | undefined; @@ -641,7 +615,7 @@ export function mapEntries(map: ReadonlyMap, f: (key: K1 export function mapEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): Map | undefined; /** @internal */ export function mapEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): Map | undefined { - if (!map) { + if (map === undefined) { return undefined; } @@ -659,10 +633,10 @@ export function some(array: readonly T[] | undefined): array is readonly T[]; export function some(array: readonly T[] | undefined, predicate: (value: T) => boolean): boolean; /** @internal */ export function some(array: readonly T[] | undefined, predicate?: (value: T) => boolean): boolean { - if (array) { - if (predicate) { - for (const v of array) { - if (predicate(v)) { + if (array !== undefined) { + if (predicate !== undefined) { + for (let i = 0; i < array.length; i++) { + if (predicate(array[i])) { return true; } } @@ -713,8 +687,8 @@ export function concatenate(array1: T[] | undefined, array2: T[] | undefined) export function concatenate(array1: readonly T[] | undefined, array2: readonly T[] | undefined): readonly T[] | undefined; /** @internal */ export function concatenate(array1: readonly T[] | undefined, array2: readonly T[] | undefined): readonly T[] | undefined { - if (!some(array2)) return array1; - if (!some(array1)) return array2; + if (array2 === undefined || array2.length === 0) return array1; + if (array1 === undefined || array1.length === 0) return array2; return [...array1, ...array2]; } @@ -751,8 +725,8 @@ function deduplicateRelational(array: readonly T[], equalityComparer: Equalit function deduplicateEquality(array: readonly T[], equalityComparer: EqualityComparer) { const result: T[] = []; - for (const item of array) { - pushIfUnique(result, item, equalityComparer); + for (let i = 0; i < array.length; i++) { + pushIfUnique(result, array[i], equalityComparer); } return result; } @@ -850,7 +824,7 @@ export function sortAndDeduplicate(array: readonly string[]): SortedReadonlyArra export function sortAndDeduplicate(array: readonly T[], comparer: Comparer, equalityComparer?: EqualityComparer): SortedReadonlyArray; /** @internal */ export function sortAndDeduplicate(array: readonly T[], comparer?: Comparer, equalityComparer?: EqualityComparer): SortedReadonlyArray { - return deduplicateSorted(sort(array, comparer), equalityComparer || comparer || compareStringsCaseSensitive as any as Comparer); + return deduplicateSorted(sort(array, comparer), equalityComparer ?? comparer ?? compareStringsCaseSensitive as any as Comparer); } /** @internal */ @@ -866,7 +840,7 @@ export function arrayIsSorted(array: readonly T[], comparer: Comparer) { /** @internal */ export function arrayIsEqualTo(array1: readonly T[] | undefined, array2: readonly T[] | undefined, equalityComparer: (a: T, b: T, index: number) => boolean = equateValues): boolean { - if (!array1 || !array2) { + if (array1 === undefined || array2 === undefined) { return array1 === array2; } @@ -899,20 +873,20 @@ export function compact(array: readonly T[]): readonly T[]; // eslint-disable /** @internal */ export function compact(array: readonly T[]): readonly T[] { let result: T[] | undefined; - if (array) { + if (array !== undefined) { for (let i = 0; i < array.length; i++) { const v = array[i]; - if (result || !v) { - if (!result) { - result = array.slice(0, i); - } + // Either the result has been initialized (and is looking to collect truthy values separately), + // or we've hit our first falsy value and need to copy over the current stretch of truthy values. + if (result ?? !v) { + result ??= array.slice(0, i); if (v) { result.push(v); } } } } - return result || array; + return result ?? array; } /** @@ -1075,7 +1049,7 @@ export function pushIfUnique(array: T[], toAdd: T, equalityComparer?: Equalit * @internal */ export function appendIfUnique(array: T[] | undefined, toAdd: T, equalityComparer?: EqualityComparer): T[] { - if (array) { + if (array !== undefined) { pushIfUnique(array, toAdd, equalityComparer); return array; } @@ -1136,7 +1110,7 @@ export function rangeEquals(array1: readonly T[], array2: readonly T[], pos: export const elementAt: (array: readonly T[] | undefined, offset: number) => T | undefined = !!Array.prototype.at ? (array, offset) => array?.at(offset) : (array, offset) => { - if (array) { + if (array !== undefined) { offset = toOffset(array, offset); if (offset < array.length) { return array[offset]; @@ -1156,7 +1130,7 @@ export function firstOrUndefined(array: readonly T[] | undefined): T | undefi /** @internal */ export function firstOrUndefinedIterator(iter: Iterable | undefined): T | undefined { - if (iter) { + if (iter !== undefined) { for (const value of iter) { return value; } @@ -1199,7 +1173,7 @@ export function last(array: readonly T[]): T { * @internal */ export function singleOrUndefined(array: readonly T[] | undefined): T | undefined { - return array && array.length === 1 + return array !== undefined && array.length === 1 ? array[0] : undefined; } @@ -1228,7 +1202,7 @@ export function singleOrMany(array: T[] | undefined): T | T[] | undefined; export function singleOrMany(array: readonly T[] | undefined): T | readonly T[] | undefined; /** @internal */ export function singleOrMany(array: readonly T[] | undefined): T | readonly T[] | undefined { - return array && array.length === 1 + return array !== undefined && array.length === 1 ? array[0] : array; } @@ -1274,7 +1248,7 @@ export function binarySearchKey(array: readonly T[], key: U, keySelector: return -1; } - let low = offset || 0; + let low = offset ?? 0; let high = array.length - 1; while (low <= high) { const middle = low + ((high - low) >> 1); @@ -1478,7 +1452,8 @@ export function arrayToMap(array: readonly T[], makeKey: (value: T) => str /** @internal */ export function arrayToMap(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V1 | V2 = identity): Map { const result = new Map(); - for (const value of array) { + for (let i = 0; i < array.length; i++) { + const value = array[i]; const key = makeKey(value); if (key !== undefined) result.set(key, makeValue(value)); } @@ -1492,7 +1467,8 @@ export function arrayToNumericMap(array: readonly T[], makeKey: (value: T) /** @internal */ export function arrayToNumericMap(array: readonly T[], makeKey: (value: T) => number, makeValue: (value: T) => T | U = identity): (T | U)[] { const result: (T | U)[] = []; - for (const value of array) { + for (let i = 0; i < array.length; i++) { + const value = array[i]; result[makeKey(value)] = makeValue(value); } return result; @@ -1505,7 +1481,8 @@ export function arrayToMultiMap(values: readonly V[], makeKey: (value: /** @internal */ export function arrayToMultiMap(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => V | U = identity): MultiMap { const result = createMultiMap(); - for (const value of values) { + for (let i = 0; i < values.length; i++) { + const value = values[i]; result.add(makeKey(value), makeValue(value)); } return result; @@ -1530,8 +1507,9 @@ export function groupBy(values: readonly T[] | undefined, keySel export function groupBy(values: readonly T[] | undefined, keySelector: (value: T) => K): { [P in K as `${P}`]?: T[]; }; // eslint-disable-line no-restricted-syntax export function groupBy(values: readonly T[] | undefined, keySelector: (value: T) => K): { [P in K as `${P}`]?: T[]; } { // eslint-disable-line no-restricted-syntax const result: Record = {}; - if (values) { - for (const value of values) { + if (values !== undefined) { + for (let i = 0; i < values.length; i++) { + const value = values[i]; const key = `${keySelector(value)}`; const array = result[key] ??= []; array.push(value); @@ -1586,7 +1564,7 @@ export function copyProperties(first: T1, second: T2) { /** @internal */ export function maybeBind(obj: T, fn: ((this: T, ...args: A) => R) | undefined): ((...args: A) => R) | undefined { - return fn ? fn.bind(obj) : undefined; + return fn?.bind(obj); } /** @internal */ @@ -1613,7 +1591,7 @@ export function createMultiMap(): MultiMap { } function multiMapAdd(this: MultiMap, key: K, value: V) { let values = this.get(key); - if (values) { + if (values !== undefined) { values.push(value); } else { @@ -1623,7 +1601,7 @@ function multiMapAdd(this: MultiMap, key: K, value: V) { } function multiMapRemove(this: MultiMap, key: K, value: V) { const values = this.get(key); - if (values) { + if (values !== undefined) { unorderedRemoveItem(values, value); if (!values.length) { this.delete(key); @@ -1633,7 +1611,7 @@ function multiMapRemove(this: MultiMap, key: K, value: V) { /** @internal */ export function createQueue(items?: readonly T[]): Queue { - const elements: (T | undefined)[] = items?.slice() || []; + const elements: (T | undefined)[] = items?.slice() ?? []; let headIndex = 0; function isEmpty() { @@ -1705,14 +1683,9 @@ export function createSet(getHashCode: (element: TElem const hash = getHashCode(element); if (!multiMap.has(hash)) return false; const candidates = multiMap.get(hash)!; - if (!isArray(candidates)) return equals(candidates, element); + if (isArray(candidates)) return contains(candidates, element, equals); - for (const candidate of candidates) { - if (equals(candidate, element)) { - return true; - } - } - return false; + return equals(candidates, element); }, add(element: TElement): Set { const hash = getHashCode(element); @@ -1779,6 +1752,8 @@ export function createSet(getHashCode: (element: TElem return size; }, forEach(action: (value: TElement, key: TElement, set: Set) => void): void { + // NOTE: arrayFrom means that if the callback mutates the underlying collection, + // we won't have an accurate set of values for (const elements of arrayFrom(multiMap.values())) { if (isArray(elements)) { for (const element of elements) { @@ -2145,6 +2120,14 @@ export function compareTextSpans(a: Partial | undefined, b: Partial(arr: readonly T[], init: number, mapper: (x: T) => number): number { + for (let i = 0; i < arr.length; i++) { + init = Math.max(init, mapper(arr[i])); + } + return init; +} + /** @internal */ export function min(items: readonly [T, ...T[]], compare: Comparer): T; /** @internal */ @@ -2270,8 +2253,8 @@ export function setUILocale(value: string | undefined) { * @internal */ export function compareStringsCaseSensitiveUI(a: string, b: string) { - const comparer = uiComparerCaseSensitive || (uiComparerCaseSensitive = createUIStringComparer(uiLocale)); - return comparer(a, b); + uiComparerCaseSensitive ??= createUIStringComparer(uiLocale); + return uiComparerCaseSensitive(a, b); } /** @internal */ @@ -2554,7 +2537,8 @@ export function findBestPatternMatch(values: readonly T[], getPattern: (value // use length of prefix as betterness criteria let longestMatchPrefixLength = -1; - for (const v of values) { + for (let i = 0; i < values.length; i++) { + const v = values[i]; const pattern = getPattern(v); if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) { longestMatchPrefixLength = pattern.prefix.length; @@ -2629,7 +2613,7 @@ export function singleElementArray(t: T | undefined): T[] | undefined { /** @internal */ export function enumerateInsertsAndDeletes(newItems: readonly T[], oldItems: readonly U[], comparer: (a: T, b: U) => Comparison, inserted: (newItem: T) => void, deleted: (oldItem: U) => void, unchanged?: (oldItem: U, newItem: T) => void) { - unchanged = unchanged || noop; + unchanged ??= noop; let newIndex = 0; let oldIndex = 0; const newLen = newItems.length; @@ -2697,7 +2681,7 @@ export function takeWhile(array: readonly T[], predicate: (eleme /** @internal */ export function takeWhile(array: readonly T[] | undefined, predicate: (element: T) => element is U): U[] | undefined; export function takeWhile(array: readonly T[] | undefined, predicate: (element: T) => element is U): U[] | undefined { - if (array) { + if (array !== undefined) { const len = array.length; let index = 0; while (index < len && predicate(array[index])) { @@ -2713,7 +2697,7 @@ export function skipWhile(array: readonly T[], predicate: (eleme export function skipWhile(array: readonly T[] | undefined, predicate: (element: T) => element is U): Exclude[] | undefined; /** @internal */ export function skipWhile(array: readonly T[] | undefined, predicate: (element: T) => element is U): Exclude[] | undefined { - if (array) { + if (array !== undefined) { const len = array.length; let index = 0; while (index < len && predicate(array[index])) { diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index baa8ce61a90f2..70c7bf1799891 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -61,6 +61,7 @@ import { LiteralType, map, MatchingKeys, + maxBy, ModifierFlags, Node, NodeArray, @@ -1126,7 +1127,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") function renderGraph() { const columnCount = columnWidths.length; - const laneCount = nodes.reduce((x, n) => Math.max(x, n.lane), 0) + 1; + const laneCount = maxBy(nodes, 0, n => n.lane) + 1; const lanes: string[] = fill(Array(laneCount), ""); const grid: (FlowGraphNode | undefined)[][] = columnWidths.map(() => Array(laneCount)); const connectors: Connection[][] = columnWidths.map(() => fill(Array(laneCount), 0)); diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index ad5f270bd4d99..e404a6eb15c94 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1,6 +1,6 @@ import { append, - arraysEqual, + arrayIsEqualTo, binarySearch, CharacterCodes, CommentDirective, @@ -475,7 +475,7 @@ export function computePositionOfLineAndCharacter(lineStarts: readonly number[], line = line < 0 ? 0 : line >= lineStarts.length ? lineStarts.length - 1 : line; } else { - Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`); + Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arrayIsEqualTo(lineStarts, computeLineStarts(debugText)) : "unknown"}`); } } diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index b2f99dd393cfb..de99268c731fe 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -74,6 +74,7 @@ import { isReferenceFileLocation, isString, last, + maxBy, maybeBind, memoize, ModuleKind, @@ -305,7 +306,7 @@ function createTabularErrorsDisplay(filesInError: (ReportFileInError | undefined const numberLength = (num: number) => Math.log(num) * Math.LOG10E + 1; const fileToErrorCount = distinctFiles.map(file => ([file, countWhere(filesInError, fileInError => fileInError!.fileName === file!.fileName)] as const)); - const maxErrors = fileToErrorCount.reduce((acc, value) => Math.max(acc, value[1] || 0), 0); + const maxErrors = maxBy(fileToErrorCount, 0, value => value[1]); const headerRow = Diagnostics.Errors_Files.message; const leftColumnHeadingLength = headerRow.split(" ")[0].length; diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index ac44525404f5d..02c27ba87faff 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2642,17 +2642,12 @@ export class TestState { if (info === undefined) return "No completion info."; const { entries } = info; - function pad(s: string, length: number) { - return s + new Array(length - s.length + 1).join(" "); - } - function max(arr: T[], selector: (x: T) => number): number { - return arr.reduce((prev, x) => Math.max(prev, selector(x)), 0); - } - const longestNameLength = max(entries, m => m.name.length); - const longestKindLength = max(entries, m => m.kind.length); + const longestNameLength = ts.maxBy(entries, 0, m => m.name.length); + const longestKindLength = ts.maxBy(entries, 0, m => m.kind.length); entries.sort((m, n) => m.sortText > n.sortText ? 1 : m.sortText < n.sortText ? -1 : m.name > n.name ? 1 : m.name < n.name ? -1 : 0); - const membersString = entries.map(m => `${pad(m.name, longestNameLength)} ${pad(m.kind, longestKindLength)} ${m.kindModifiers} ${m.isRecommended ? "recommended " : ""}${m.source === undefined ? "" : m.source}`).join("\n"); - Harness.IO.log(membersString); + + const formattedEntries = entries.map(m => `${m.name.padEnd(longestNameLength)} ${m.kind.padEnd(longestKindLength)} ${m.kindModifiers} ${m.isRecommended ? "recommended " : ""}${m.source ?? ""}`); + Harness.IO.log(formattedEntries.join("\n")); } public printContext() { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index ae10458e5ad79..f6c0fa716f4fc 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -76,7 +76,6 @@ import { LanguageServiceMode, length, map, - mapDefinedEntries, mapDefinedIterator, missingFileModifiedTime, MultiMap, @@ -4273,14 +4272,15 @@ export class ProjectService { /** @internal */ loadAncestorProjectTree(forProjects?: ReadonlyCollection) { - forProjects = forProjects || mapDefinedEntries( - this.configuredProjects, - (key, project) => !project.isInitialLoadPending() ? [key, true] : undefined, + forProjects ??= new Set( + mapDefinedIterator(this.configuredProjects.entries(), ([key, project]) => !project.isInitialLoadPending() ? key : undefined), ); const seenProjects = new Set(); - // Work on array copy as we could add more projects as part of callback - for (const project of arrayFrom(this.configuredProjects.values())) { + // We must copy the current configured projects into a separate array, + // as we could end up creating and adding more projects indirectly. + const currentConfiguredProjects = arrayFrom(this.configuredProjects.values()); + for (const project of currentConfiguredProjects) { // If this project has potential project reference for any of the project we are loading ancestor tree for // load this project first if (forEachPotentialProjectReference(project, potentialRefPath => forProjects.has(potentialRefPath))) { diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 94664ce5d4332..cbefd89bf8403 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -1,5 +1,5 @@ import { - arraysEqual, + arrayIsEqualTo, ArrowFunction, AssignmentDeclarationKind, BinaryExpression, @@ -222,7 +222,7 @@ export function getJsDocCommentsFromDeclarations(declarations: readonly Declarat } function isIdenticalListOfDisplayParts(parts1: SymbolDisplayPart[], parts2: SymbolDisplayPart[]) { - return arraysEqual(parts1, parts2, (p1, p2) => p1.kind === p2.kind && p1.text === p2.text); + return arrayIsEqualTo(parts1, parts2, (p1, p2) => p1.kind === p2.kind && p1.text === p2.text); } function getCommentHavingNodes(declaration: Declaration): readonly (JSDoc | JSDocTag)[] {