From 732632fbf2e80535dbc3adc3186a8035bc4ab66c Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 11 Jul 2017 08:08:45 -0700 Subject: [PATCH 1/3] Convert most of core.ts to accept ReadonlyArray --- src/compiler/checker.ts | 4 +- src/compiler/commandLineParser.ts | 4 +- src/compiler/core.ts | 149 ++++++++++-------- src/compiler/sys.ts | 6 +- src/compiler/transformers/es2015.ts | 2 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/transformers/ts.ts | 2 +- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 2 +- src/harness/fourslash.ts | 2 +- src/harness/harness.ts | 2 +- src/harness/harnessLanguageService.ts | 2 +- src/harness/loggedIO.ts | 8 +- .../unittests/tsserverProjectSystem.ts | 14 +- src/harness/virtualFileSystem.ts | 2 +- src/server/builder.ts | 2 +- src/server/lsHost.ts | 2 +- src/services/jsTyping.ts | 2 +- src/services/pathCompletions.ts | 8 +- src/services/shims.ts | 4 +- src/services/types.ts | 2 +- 21 files changed, 117 insertions(+), 106 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a637e0e3ab23..92a19d6c126b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2010,7 +2010,7 @@ namespace ts { } function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] { - if (contains(visitedSymbolTables, symbols)) { + if (contains(visitedSymbolTables, symbols)) { return undefined; } visitedSymbolTables.push(symbols); @@ -20911,7 +20911,7 @@ namespace ts { } } - function areTypeParametersIdentical(declarations: (ClassDeclaration | InterfaceDeclaration)[], typeParameters: TypeParameter[]) { + function areTypeParametersIdentical(declarations: ReadonlyArray, typeParameters: TypeParameter[]) { const maxTypeArgumentCount = length(typeParameters); const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 0f9eba8d4fe28..4f1210793bfc5 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -2136,7 +2136,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: ReadonlyArray, keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions); for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { @@ -2158,7 +2158,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: ReadonlyArray, keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions); for (let i = nextExtensionPriority; i < extensions.length; i++) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 745cce8a6cd9d..8b1d31ec6e7c3 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -53,7 +53,7 @@ namespace ts { } /* @internal */ - export function createSymbolTable(symbols?: Symbol[]): SymbolTable { + export function createSymbolTable(symbols?: ReadonlyArray): SymbolTable { const result = createMap() as SymbolTable; if (symbols) { for (const symbol of symbols) { @@ -88,7 +88,7 @@ namespace ts { class MapIterator { private data: MapLike; - private keys: string[]; + private keys: ReadonlyArray; private index = 0; private selector: (data: MapLike, key: string) => U; constructor(data: MapLike, selector: (data: MapLike, key: string) => U) { @@ -175,7 +175,7 @@ namespace ts { GreaterThan = 1 } - export function length(array: any[]) { + export function length(array: ReadonlyArray) { return array ? array.length : 0; } @@ -184,7 +184,7 @@ namespace ts { * returns a truthy value, then returns that value. * If no such value is found, the callback is applied to each element of array and undefined is returned. */ - export function forEach(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { + export function forEach(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { if (array) { for (let i = 0; i < array.length; i++) { const result = callback(array[i], i); @@ -217,14 +217,14 @@ namespace ts { return undefined; } - export function zipWith(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void { + export function zipWith(arrayA: ReadonlyArray, arrayB: ReadonlyArray, callback: (a: T, b: U, index: number) => void): void { Debug.assert(arrayA.length === arrayB.length); for (let i = 0; i < arrayA.length; i++) { callback(arrayA[i], arrayB[i], i); } } - export function zipToMap(keys: string[], values: T[]): Map { + export function zipToMap(keys: ReadonlyArray, values: ReadonlyArray): Map { Debug.assert(keys.length === values.length); const map = createMap(); for (let i = 0; i < keys.length; ++i) { @@ -238,7 +238,7 @@ namespace ts { * returns a falsey value, then returns false. * If no such value is found, the callback is applied to each element of array and `true` is returned. */ - export function every(array: T[], callback: (element: T, index: number) => boolean): boolean { + export function every(array: ReadonlyArray, callback: (element: T, index: number) => boolean): boolean { if (array) { for (let i = 0; i < array.length; i++) { if (!callback(array[i], i)) { @@ -251,7 +251,7 @@ namespace ts { } /** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */ - export function find(array: T[], predicate: (element: T, index: number) => boolean): T | undefined { + export function find(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined { for (let i = 0; i < array.length; i++) { const value = array[i]; if (predicate(value, i)) { @@ -262,7 +262,7 @@ namespace ts { } /** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */ - export function findIndex(array: T[], predicate: (element: T, index: number) => boolean): number { + export function findIndex(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): number { for (let i = 0; i < array.length; i++) { if (predicate(array[i], i)) { return i; @@ -275,7 +275,7 @@ namespace ts { * Returns the first truthy result of `callback`, or else fails. * This is like `forEach`, but never returns undefined. */ - export function findMap(array: T[], callback: (element: T, index: number) => U | undefined): U { + export function findMap(array: ReadonlyArray, callback: (element: T, index: number) => U | undefined): U { for (let i = 0; i < array.length; i++) { const result = callback(array[i], i); if (result) { @@ -285,7 +285,7 @@ namespace ts { Debug.fail(); } - export function contains(array: T[], value: T): boolean { + export function contains(array: ReadonlyArray, value: T): boolean { if (array) { for (const v of array) { if (v === value) { @@ -296,7 +296,7 @@ namespace ts { return false; } - export function indexOf(array: T[], value: T): number { + export function indexOf(array: ReadonlyArray, value: T): number { if (array) { for (let i = 0; i < array.length; i++) { if (array[i] === value) { @@ -307,7 +307,7 @@ namespace ts { return -1; } - export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number { + export function indexOfAnyCharCode(text: string, charCodes: ReadonlyArray, start?: number): number { for (let i = start || 0; i < text.length; i++) { if (contains(charCodes, text.charCodeAt(i))) { return i; @@ -316,7 +316,7 @@ namespace ts { return -1; } - export function countWhere(array: T[], predicate: (x: T, i: number) => boolean): number { + export function countWhere(array: ReadonlyArray, predicate: (x: T, i: number) => boolean): number { let count = 0; if (array) { for (let i = 0; i < array.length; i++) { @@ -335,6 +335,8 @@ namespace ts { */ export function filter(array: T[], f: (x: T) => x is U): U[]; export function filter(array: T[], f: (x: T) => boolean): T[]; + export function filter(array: ReadonlyArray, f: (x: T) => x is U): ReadonlyArray; + export function filter(array: ReadonlyArray, f: (x: T) => boolean): ReadonlyArray; export function filter(array: T[], f: (x: T) => boolean): T[] { if (array) { const len = array.length; @@ -382,7 +384,7 @@ namespace ts { array.length = outIndex; } - export function map(array: T[], f: (x: T, i: number) => U): U[] { + export function map(array: ReadonlyArray, f: (x: T, i: number) => U): U[] { let result: U[]; if (array) { result = []; @@ -394,6 +396,8 @@ namespace ts { } // Maps from T to T and avoids allocation if all elements map to themselves + export function sameMap(array: T[], f: (x: T, i: number) => T): T[]; + export function sameMap(array: ReadonlyArray, f: (x: T, i: number) => T): ReadonlyArray; export function sameMap(array: T[], f: (x: T, i: number) => T): T[] { let result: T[]; if (array) { @@ -419,7 +423,7 @@ namespace ts { * * @param array The array to flatten. */ - export function flatten(array: (T | T[])[]): T[] { + export function flatten(array: ReadonlyArray>): T[] { let result: T[]; if (array) { result = []; @@ -429,7 +433,7 @@ namespace ts { addRange(result, v); } else { - result.push(v); + result.push(v as T); } } } @@ -444,7 +448,7 @@ namespace ts { * @param array The array to map. * @param mapfn The callback used to map the result into one or more values. */ - export function flatMap(array: T[] | undefined, mapfn: (x: T, i: number) => U | U[] | undefined): U[] | undefined { + export function flatMap(array: ReadonlyArray | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray | undefined): U[] | undefined { let result: U[]; if (array) { result = []; @@ -455,7 +459,7 @@ namespace ts { addRange(result, v); } else { - result.push(v); + result.push(v as U); } } } @@ -470,6 +474,8 @@ namespace ts { * @param array The array to map. * @param mapfn The callback used to map the result into one or more values. */ + export function sameFlatMap(array: T[], mapfn: (x: T, i: number) => T | ReadonlyArray): T[]; + export function sameFlatMap(array: ReadonlyArray, mapfn: (x: T, i: number) => T | ReadonlyArray): ReadonlyArray; export function sameFlatMap(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] { let result: T[]; if (array) { @@ -508,7 +514,7 @@ namespace ts { * Computes the first matching span of elements and returns a tuple of the first span * and the remaining elements. */ - export function span(array: T[], f: (x: T, i: number) => boolean): [T[], T[]] { + export function span(array: ReadonlyArray, f: (x: T, i: number) => boolean): [T[], T[]] { if (array) { for (let i = 0; i < array.length; i++) { if (!f(array[i], i)) { @@ -528,7 +534,7 @@ namespace ts { * @param keyfn A callback used to select the key for an element. * @param mapfn A callback used to map a contiguous chunk of values to a single value. */ - export function spanMap(array: T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] { + export function spanMap(array: ReadonlyArray, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] { let result: U[]; if (array) { result = []; @@ -581,7 +587,7 @@ namespace ts { return result; } - export function some(array: T[], predicate?: (value: T) => boolean): boolean { + export function some(array: ReadonlyArray, predicate?: (value: T) => boolean): boolean { if (array) { if (predicate) { for (const v of array) { @@ -597,6 +603,8 @@ namespace ts { return false; } + export function concatenate(array1: T[], array2: T[]): T[]; + export function concatenate(array1: ReadonlyArray, array2: ReadonlyArray): ReadonlyArray; export function concatenate(array1: T[], array2: T[]): T[] { if (!some(array2)) return array1; if (!some(array1)) return array2; @@ -604,7 +612,7 @@ namespace ts { } // TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication. - export function deduplicate(array: T[], areEqual?: (a: T, b: T) => boolean): T[] { + export function deduplicate(array: ReadonlyArray, areEqual?: (a: T, b: T) => boolean): T[] { let result: T[]; if (array) { result = []; @@ -661,6 +669,8 @@ namespace ts { /** * Compacts an array, removing any falsey elements. */ + export function compact(array: T[]): T[]; + export function compact(array: ReadonlyArray): ReadonlyArray; export function compact(array: T[]): T[] { let result: T[]; if (array) { @@ -727,7 +737,7 @@ namespace ts { * Gets the actual offset into an array for a relative offset. Negative offsets indicate a * position offset from the end of the array. */ - function toOffset(array: any[], offset: number) { + function toOffset(array: ReadonlyArray, offset: number) { return offset < 0 ? array.length + offset : offset; } @@ -741,7 +751,7 @@ namespace ts { * @param start The offset in `from` at which to start copying values. * @param end The offset in `from` at which to stop copying values (non-inclusive). */ - export function addRange(to: T[] | undefined, from: T[] | undefined, start?: number, end?: number): T[] | undefined { + export function addRange(to: T[] | undefined, from: ReadonlyArray | undefined, start?: number, end?: number): T[] | undefined { if (from === undefined) return to; if (to === undefined) return from.slice(start, end); start = start === undefined ? 0 : toOffset(from, start); @@ -758,14 +768,14 @@ namespace ts { /** * Stable sort of an array. Elements equal to each other maintain their relative position in the array. */ - export function stableSort(array: T[], comparer: (x: T, y: T) => Comparison = compareValues) { + export function stableSort(array: ReadonlyArray, comparer: (x: T, y: T) => Comparison = compareValues) { return array .map((_, i) => i) // create array of indices .sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position .map(i => array[i]); // get sorted array } - export function rangeEquals(array1: T[], array2: T[], pos: number, end: number) { + export function rangeEquals(array1: ReadonlyArray, array2: ReadonlyArray, pos: number, end: number) { while (pos < end) { if (array1[pos] !== array2[pos]) { return false; @@ -779,7 +789,7 @@ namespace ts { * Returns the element at a specific offset in an array if non-empty, `undefined` otherwise. * A negative offset indicates the element should be retrieved from the end of the array. */ - export function elementAt(array: T[] | undefined, offset: number): T | undefined { + export function elementAt(array: ReadonlyArray | undefined, offset: number): T | undefined { if (array) { offset = toOffset(array, offset); if (offset < array.length) { @@ -792,21 +802,21 @@ namespace ts { /** * Returns the first element of an array if non-empty, `undefined` otherwise. */ - export function firstOrUndefined(array: T[]): T | undefined { + export function firstOrUndefined(array: ReadonlyArray): T | undefined { return elementAt(array, 0); } /** * Returns the last element of an array if non-empty, `undefined` otherwise. */ - export function lastOrUndefined(array: T[]): T | undefined { + export function lastOrUndefined(array: ReadonlyArray): T | undefined { return elementAt(array, -1); } /** * Returns the only element of an array if it contains only one element, `undefined` otherwise. */ - export function singleOrUndefined(array: T[]): T | undefined { + export function singleOrUndefined(array: ReadonlyArray): T | undefined { return array && array.length === 1 ? array[0] : undefined; @@ -816,13 +826,15 @@ namespace ts { * Returns the only element of an array if it contains only one element; otheriwse, returns the * array. */ + export function singleOrMany(array: T[]): T | T[]; + export function singleOrMany(array: ReadonlyArray): T | ReadonlyArray; export function singleOrMany(array: T[]): T | T[] { return array && array.length === 1 ? array[0] : array; } - export function replaceElement(array: T[], index: number, value: T): T[] { + export function replaceElement(array: ReadonlyArray, index: number, value: T): T[] { const result = array.slice(0); result[index] = value; return result; @@ -835,7 +847,7 @@ namespace ts { * @param array A sorted array whose first element must be no larger than number * @param number The value to be searched for in the array. */ - export function binarySearch(array: T[], value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number { + export function binarySearch(array: ReadonlyArray, value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number { if (!array || array.length === 0) { return -1; } @@ -864,8 +876,8 @@ namespace ts { return ~low; } - export function reduceLeft(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; - export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceLeft(array: ReadonlyArray, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; + export function reduceLeft(array: ReadonlyArray, f: (memo: T, value: T, i: number) => T): T; export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T { if (array && array.length > 0) { const size = array.length; @@ -890,8 +902,8 @@ namespace ts { return initial; } - export function reduceRight(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; - export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceRight(array: ReadonlyArray, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; + export function reduceRight(array: ReadonlyArray, f: (memo: T, value: T, i: number) => T): T; export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T { if (array) { const size = array.length; @@ -1058,9 +1070,9 @@ namespace ts { * the same key with the given 'makeKey' function, then the element with the higher * index in the array will be the one associated with the produced key. */ - export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { + export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string): Map; + export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string, makeValue: (value: T) => U): Map; + export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { const result = createMap(); for (const value of array) { result.set(makeKey(value), makeValue ? makeValue(value) : value); @@ -1073,9 +1085,9 @@ namespace ts { * * @param array the array of input elements. */ - export function arrayToSet(array: string[]): Map; - export function arrayToSet(array: T[], makeKey: (value: T) => string): Map; - export function arrayToSet(array: any[], makeKey?: (value: any) => string): Map { + export function arrayToSet(array: ReadonlyArray): Map; + export function arrayToSet(array: ReadonlyArray, makeKey: (value: T) => string): Map; + export function arrayToSet(array: ReadonlyArray, makeKey?: (value: any) => string): Map { return arrayToMap(array, makeKey || (s => s), () => true); } @@ -1628,7 +1640,7 @@ namespace ts { return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory)); } - export function getNormalizedPathFromPathComponents(pathComponents: string[]) { + export function getNormalizedPathFromPathComponents(pathComponents: ReadonlyArray) { if (pathComponents && pathComponents.length) { return pathComponents[0] + pathComponents.slice(1).join(directorySeparator); } @@ -1830,7 +1842,7 @@ namespace ts { } /* @internal */ - export function fileExtensionIsOneOf(path: string, extensions: string[]): boolean { + export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray): boolean { for (const extension of extensions) { if (fileExtensionIs(path, extension)) { return true; @@ -1855,7 +1867,7 @@ namespace ts { const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; const singleAsteriskRegexFragmentOther = "[^/]*"; - export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { + export function getRegularExpressionForWildcard(specs: ReadonlyArray, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { const patterns = getRegularExpressionsForWildcards(specs, basePath, usage); if (!patterns || !patterns.length) { return undefined; @@ -1867,7 +1879,7 @@ namespace ts { return `^(${pattern})${terminator}`; } - function getRegularExpressionsForWildcards(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined { + function getRegularExpressionsForWildcards(specs: ReadonlyArray, basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined { if (specs === undefined || specs.length === 0) { return undefined; } @@ -1972,21 +1984,21 @@ namespace ts { } export interface FileSystemEntries { - files: string[]; - directories: string[]; + files: ReadonlyArray; + directories: ReadonlyArray; } export interface FileMatcherPatterns { /** One pattern for each "include" spec. */ - includeFilePatterns: string[]; + includeFilePatterns: ReadonlyArray; /** One pattern matching one of any of the "include" specs. */ includeFilePattern: string; includeDirectoryPattern: string; excludePattern: string; - basePaths: string[]; + basePaths: ReadonlyArray; } - export function getFileMatcherPatterns(path: string, excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns { + export function getFileMatcherPatterns(path: string, excludes: ReadonlyArray, includes: ReadonlyArray, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns { path = normalizePath(path); currentDirectory = normalizePath(currentDirectory); const absolutePath = combinePaths(currentDirectory, path); @@ -2000,7 +2012,7 @@ namespace ts { }; } - export function matchFiles(path: string, extensions: string[], excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] { + export function matchFiles(path: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] { path = normalizePath(path); currentDirectory = normalizePath(currentDirectory); @@ -2020,7 +2032,7 @@ namespace ts { visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); } - return flatten(results); + return flatten(results); function visitDirectory(path: string, absolutePath: string, depth: number | undefined) { let { files, directories } = getFileSystemEntries(path); @@ -2064,7 +2076,7 @@ namespace ts { /** * Computes the unique non-wildcard base paths amongst the provided include patterns. */ - function getBasePaths(path: string, includes: string[], useCaseSensitiveFileNames: boolean) { + function getBasePaths(path: string, includes: ReadonlyArray, useCaseSensitiveFileNames: boolean) { // Storage for our results in the form of literal paths (e.g. the paths as written by the user). const basePaths: string[] = [path]; @@ -2136,13 +2148,13 @@ namespace ts { /** * List of supported extensions in order of file resolution precedence. */ - export const supportedTypeScriptExtensions = [Extension.Ts, Extension.Tsx, Extension.Dts]; + export const supportedTypeScriptExtensions : ReadonlyArray= [Extension.Ts, Extension.Tsx, Extension.Dts]; /** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */ - export const supportedTypescriptExtensionsForExtractExtension = [Extension.Dts, Extension.Ts, Extension.Tsx]; - export const supportedJavascriptExtensions = [Extension.Js, Extension.Jsx]; - const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); + export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray = [Extension.Dts, Extension.Ts, Extension.Tsx]; + export const supportedJavascriptExtensions: ReadonlyArray = [Extension.Js, Extension.Jsx]; + const allSupportedExtensions = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions]; - export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]): string[] { + export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray): ReadonlyArray { const needAllExtensions = options && options.allowJs; if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) { return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions; @@ -2164,7 +2176,7 @@ namespace ts { return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension)); } - export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]) { + export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: ReadonlyArray) { if (!fileName) { return false; } for (const extension of getSupportedExtensions(compilerOptions, extraFileExtensions)) { @@ -2188,7 +2200,7 @@ namespace ts { Lowest = DeclarationAndJavaScriptFiles, } - export function getExtensionPriority(path: string, supportedExtensions: string[]): ExtensionPriority { + export function getExtensionPriority(path: string, supportedExtensions: ReadonlyArray): ExtensionPriority { for (let i = supportedExtensions.length - 1; i >= 0; i--) { if (fileExtensionIs(path, supportedExtensions[i])) { return adjustExtensionPriority(i, supportedExtensions); @@ -2203,7 +2215,7 @@ namespace ts { /** * Adjusts an extension priority to be the highest priority within the same range. */ - export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority { + export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray): ExtensionPriority { if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) { return ExtensionPriority.TypeScriptFiles; } @@ -2212,12 +2224,13 @@ namespace ts { } else { return supportedExtensions.length; - } } + } + } /** * Gets the next lowest extension priority for a given priority. */ - export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority { + export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray): ExtensionPriority { if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) { return ExtensionPriority.DeclarationAndJavaScriptFiles; } @@ -2406,7 +2419,7 @@ namespace ts { * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.) */ /* @internal */ - export function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined { + export function matchPatternOrExact(patternStrings: ReadonlyArray, candidate: string): string | Pattern | undefined { const patterns: Pattern[] = []; for (const patternString of patternStrings) { const pattern = tryParsePattern(patternString); @@ -2439,7 +2452,7 @@ namespace ts { /** Return the object corresponding to the best pattern to match `candidate`. */ /* @internal */ - export function findBestPatternMatch(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { + export function findBestPatternMatch(values: ReadonlyArray, getPattern: (value: T) => Pattern, candidate: string): T | undefined { let matchedValue: T | undefined = undefined; // use length of prefix as betterness criteria let longestMatchPrefixLength = -1; @@ -2495,7 +2508,7 @@ namespace ts { Debug.fail(`File ${path} has unknown extension.`); } export function tryGetExtensionFromPath(path: string): Extension | undefined { - return find(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e)); + return find(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e)); } export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index d5102da0b3fa6..3e3ff90c00cd4 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -39,7 +39,7 @@ namespace ts { getExecutingFilePath(): string; getCurrentDirectory(): string; getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -100,7 +100,7 @@ namespace ts { readFile(path: string): string; writeFile(path: string, contents: string): void; getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: string[], basePaths?: string[], excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, basePaths?: ReadonlyArray, excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[]; watchFile?(path: string, callback: FileWatcherCallback): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; realpath(path: string): string; @@ -287,7 +287,7 @@ namespace ts { } } - function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[], depth?: number): string[] { + function readDirectory(path: string, extensions?: ReadonlyArray, excludes?: ReadonlyArray, includes?: ReadonlyArray, depth?: number): string[] { return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries); } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 3de734401939d..19f9d00c65623 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3579,7 +3579,7 @@ namespace ts { // Map spans of spread expressions into their expressions and spans of other // expressions into an array literal. const numElements = elements.length; - const segments = flatten( + const segments = flatten( spanMap(elements, partitionSpread, (partition, visitPartition, _start, end) => visitPartition(partition, multiLine, hasTrailingComma && end === numElements) ) diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index eab5f69bd4f23..ca22cb701d0c5 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -88,7 +88,7 @@ namespace ts { else { // Map spans of JsxAttribute nodes into object literals and spans // of JsxSpreadAttribute nodes into expressions. - const segments = flatten( + const segments = flatten( spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread ? map(attrs, transformJsxSpreadAttributeToExpression) : createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement)) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 5d6efa489b549..5fd1b7b62daff 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1377,7 +1377,7 @@ namespace ts { const decoratorExpressions: Expression[] = []; addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); - addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); + addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); addTypeMetadata(node, container, decoratorExpressions); return decoratorExpressions; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index aefcce6d0bb63..35320318ef9e4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2406,7 +2406,7 @@ namespace ts { export interface ParseConfigHost { useCaseSensitiveFileNames: boolean; - readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[]; + readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth: number): string[]; /** * Gets a value indicating whether the specified path exists and is a file. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 280a2f73a0e57..77e3b776abd69 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3334,7 +3334,7 @@ namespace ts { } } - return stableSort(result, (x, y) => compareValues(x[0], y[0])); + return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0])); } export function formatSyntaxKind(kind: SyntaxKind): string { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 411f6762ba31d..2dc438c1be057 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -219,7 +219,7 @@ namespace FourSlash { // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified - private addMatchedInputFile(referenceFilePath: string, extensions: string[]) { + private addMatchedInputFile(referenceFilePath: string, extensions: ReadonlyArray) { const inputFiles = this.inputFiles; const languageServiceAdapterHost = this.languageServiceAdapterHost; if (!extensions) { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 026ff2de85f9d..d5218695f09aa 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -493,7 +493,7 @@ namespace Harness { args(): string[]; getExecutingFilePath(): string; exit(exitCode?: number): void; - readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number): string[]; + readDirectory(path: string, extension?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; tryEnableSourceMapsForHost?(): void; getEnvironmentVariable?(name: string): string; } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 82f0b87be9402..339138ba30f91 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -208,7 +208,7 @@ namespace Harness.LanguageService { const script = this.getScriptSnapshot(fileName); return script !== undefined; } - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { return ts.matchFiles(path, extensions, exclude, include, /*useCaseSensitiveFileNames*/ false, this.getCurrentDirectory(), diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts index d4a1e2e2bd09c..f113b9ddb9db8 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -64,11 +64,11 @@ interface IOLog { }[]; directoriesRead: { path: string, - extensions: string[], - exclude: string[], - include: string[], + extensions: ReadonlyArray, + exclude: ReadonlyArray, + include: ReadonlyArray, depth: number, - result: string[] + result: ReadonlyArray, }[]; } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index b34669c26907a..3dde03a092b5c 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -438,24 +438,22 @@ namespace ts.projectSystem { } } - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => { - const result: FileSystemEntries = { - directories: [], - files: [] - }; + const directories: string[] = []; + const files: string[] = []; const dirEntry = this.fs.get(this.toPath(dir)); if (isFolder(dirEntry)) { dirEntry.entries.forEach((entry) => { if (isFolder(entry)) { - result.directories.push(entry.fullPath); + directories.push(entry.fullPath); } else if (isFile(entry)) { - result.files.push(entry.fullPath); + files.push(entry.fullPath); } }); } - return result; + return { directories, files }; }); } diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 3904a27bb1c22..da7784770dff3 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -216,7 +216,7 @@ namespace Utils { } } - readDirectory(path: string, extensions: string[], excludes: string[], includes: string[], depth: number) { + readDirectory(path: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth: number) { return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, (path: string) => this.getAccessibleFileSystemEntries(path)); } } diff --git a/src/server/builder.ts b/src/server/builder.ts index 7d35247e97480..8fd8fbf1e6543 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -277,7 +277,7 @@ namespace ts.server { const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path); if (referencedFilePaths.length > 0) { - return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos); + return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos); } return []; } diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index 55513f1a28505..620697e1742d8 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -225,7 +225,7 @@ namespace ts.server { return this.host.directoryExists(path); } - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { return this.host.readDirectory(path, extensions, exclude, include, depth); } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index ff90d5bdb3489..60b29b81883c3 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -12,7 +12,7 @@ namespace ts.JsTyping { directoryExists: (path: string) => boolean; fileExists: (fileName: string) => boolean; readFile: (path: string, encoding?: string) => string; - readDirectory: (rootDir: string, extensions: string[], excludes: string[], includes: string[], depth?: number) => string[]; + readDirectory: (rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth?: number) => string[]; } interface PackageJson { diff --git a/src/services/pathCompletions.ts b/src/services/pathCompletions.ts index bc94fb7dbc5ed..fbd4a22ec29d8 100644 --- a/src/services/pathCompletions.ts +++ b/src/services/pathCompletions.ts @@ -52,7 +52,7 @@ namespace ts.Completions.PathCompletions { return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); } - function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] { + function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] { const basePath = compilerOptions.project || host.getCurrentDirectory(); const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase); @@ -69,7 +69,7 @@ namespace ts.Completions.PathCompletions { /** * Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename. */ - function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { if (fragment === undefined) { fragment = ""; } @@ -190,7 +190,7 @@ namespace ts.Completions.PathCompletions { return result; } - function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[], host: LanguageServiceHost): string[] { + function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: ReadonlyArray, host: LanguageServiceHost): string[] { if (host.readDirectory) { const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined; if (parsed) { @@ -505,7 +505,7 @@ namespace ts.Completions.PathCompletions { return tryIOAndConsumeErrors(host, host.getDirectories, directoryName); } - function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { + function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray): string[] { return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include); } diff --git a/src/services/shims.ts b/src/services/shims.ts index 0a0b2c82c5bc5..4ecd2e4e9b896 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -444,7 +444,7 @@ namespace ts { return this.shimHost.getDefaultLibFileName(JSON.stringify(options)); } - public readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + public readDirectory(path: string, extensions?: ReadonlyArray, exclude?: string[], include?: string[], depth?: number): string[] { const pattern = getFileMatcherPatterns(path, exclude, include, this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory()); return JSON.parse(this.shimHost.readDirectory( @@ -483,7 +483,7 @@ namespace ts { } } - public readDirectory(rootDir: string, extensions: string[], exclude: string[], include: string[], depth?: number): string[] { + public readDirectory(rootDir: string, extensions: ReadonlyArray, exclude: ReadonlyArray, include: ReadonlyArray, depth?: number): string[] { const pattern = getFileMatcherPatterns(rootDir, exclude, include, this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory()); return JSON.parse(this.shimHost.readDirectory( diff --git a/src/services/types.ts b/src/services/types.ts index d440ecab28ece..bfb8a80b76913 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -164,7 +164,7 @@ namespace ts { * LS host can optionally implement these methods to support completions for module specifiers. * Without these methods, only completions for ambient modules will be provided. */ - readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[]; + readDirectory?(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; readFile?(path: string, encoding?: string): string; fileExists?(path: string): boolean; From 6e07de798e483b05f817823d79bfc6938ec7ab98 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 11 Jul 2017 11:26:02 -0700 Subject: [PATCH 2/3] Fix lint --- src/compiler/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 8b1d31ec6e7c3..9b2523aa2be52 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2148,7 +2148,7 @@ namespace ts { /** * List of supported extensions in order of file resolution precedence. */ - export const supportedTypeScriptExtensions : ReadonlyArray= [Extension.Ts, Extension.Tsx, Extension.Dts]; + export const supportedTypeScriptExtensions: ReadonlyArray = [Extension.Ts, Extension.Tsx, Extension.Dts]; /** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */ export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray = [Extension.Dts, Extension.Ts, Extension.Tsx]; export const supportedJavascriptExtensions: ReadonlyArray = [Extension.Js, Extension.Jsx]; From 52ce0aa0434909614ee22d4396d696c0bcf81a38 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 11 Jul 2017 13:32:54 -0700 Subject: [PATCH 3/3] Fix isArray --- src/compiler/core.ts | 6 +++--- src/harness/fourslash.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 9b2523aa2be52..efa354b4963eb 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -433,7 +433,7 @@ namespace ts { addRange(result, v); } else { - result.push(v as T); + result.push(v); } } } @@ -459,7 +459,7 @@ namespace ts { addRange(result, v); } else { - result.push(v as U); + result.push(v); } } } @@ -1170,7 +1170,7 @@ namespace ts { /** * Tests whether a value is an array. */ - export function isArray(value: any): value is any[] { + export function isArray(value: any): value is ReadonlyArray { return Array.isArray ? Array.isArray(value) : value instanceof Array; } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 2dc438c1be057..a25ba37dc2636 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -605,7 +605,7 @@ namespace FourSlash { this.verifyGoToXPlain(arg0, endMarkerNames, getDefs); } else if (ts.isArray(arg0)) { - const pairs: [string | string[], string | string[]][] = arg0; + const pairs: ReadonlyArray<[string | string[], string | string[]]> = arg0; for (const [start, end] of pairs) { this.verifyGoToXPlain(start, end, getDefs); }