diff --git a/Jakefile.js b/Jakefile.js index 15fe40141f..2dcba9f97e 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -91,6 +91,7 @@ var languageServiceLibrarySources = filesFromConfig(path.join(serverDirectory, " var harnessCoreSources = [ "harness.ts", "virtualFileSystem.ts", + "virtualFileSystemWithWatch.ts", "sourceMapRecorder.ts", "harnessLanguageService.ts", "fourslash.ts", @@ -128,6 +129,7 @@ var harnessSources = harnessCoreSources.concat([ "convertCompilerOptionsFromJson.ts", "convertTypeAcquisitionFromJson.ts", "tsserverProjectSystem.ts", + "tscWatchMode.ts", "compileOnSave.ts", "typingsInstaller.ts", "projectErrors.ts", diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts new file mode 100644 index 0000000000..4fb90ca714 --- /dev/null +++ b/src/compiler/builder.ts @@ -0,0 +1,492 @@ +/// + +namespace ts { + export interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + + export interface EmitOutputDetailed extends EmitOutput { + diagnostics: Diagnostic[]; + sourceMaps: SourceMapData[]; + emittedSourceFiles: SourceFile[]; + } + + export interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } + + export interface ChangedProgramFiles { + /** Minimal set of list of files that require emit */ + readonly filesToEmit: ReadonlyArray; + /** File paths of source files changed/added/removed or affected by changed files */ + readonly changedFiles: ReadonlyArray; + } + + export interface Builder { + /** + * This is the callback when file infos in the builder are updated + */ + onProgramUpdateGraph(program: Program, hasInvalidatedResolution: HasInvalidatedResolution): void; + getFilesAffectedBy(program: Program, path: Path): string[]; + emitFile(program: Program, path: Path): EmitOutput; + + /** Emit the changed files and clear the cache of the changed files */ + emitChangedFiles(program: Program): EmitOutputDetailed[]; + /** Get the changed files since last query and then clear the cache of changed files */ + getChangedProgramFiles(program: Program): ChangedProgramFiles; + /** When called gets the semantic diagnostics for the program. It also caches the diagnostics and manage them */ + getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[]; + + /** Called to reset the status of the builder */ + clear(): void; + } + + interface EmitHandler { + addScriptInfo(program: Program, sourceFile: SourceFile): void; + removeScriptInfo(path: Path): void; + updateScriptInfo(program: Program, sourceFile: SourceFile): void; + /** + * Gets the files affected by the script info which has updated shape from the known one + */ + getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile, singleFileResult: string[]): string[]; + } + + export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean, + cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput | EmitOutputDetailed { + const outputFiles: OutputFile[] = []; + let emittedSourceFiles: SourceFile[]; + const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); + if (!isDetailed) { + return { outputFiles, emitSkipped: emitResult.emitSkipped }; + } + + return { + outputFiles, + emitSkipped: emitResult.emitSkipped, + diagnostics: emitResult.diagnostics, + sourceMaps: emitResult.sourceMaps, + emittedSourceFiles + }; + + function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, _onError: (message: string) => void, sourceFiles: SourceFile[]) { + outputFiles.push({ name: fileName, writeByteOrderMark, text }); + if (isDetailed) { + emittedSourceFiles = addRange(emittedSourceFiles, sourceFiles); + } + } + } + + export function createBuilder( + getCanonicalFileName: (fileName: string) => string, + getEmitOutput: (program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean) => EmitOutput | EmitOutputDetailed, + computeHash: (data: string) => string, + shouldEmitFile: (sourceFile: SourceFile) => boolean + ): Builder { + let isModuleEmit: boolean | undefined; + // Last checked shape signature for the file info + type FileInfo = { fileName: string; version: string; signature: string; }; + const fileInfos = createMap(); + const semanticDiagnosticsPerFile = createMap(); + /** The map has key by source file's path that has been changed */ + const changedFileNames = createMap(); + let emitHandler: EmitHandler; + return { + onProgramUpdateGraph, + getFilesAffectedBy, + emitFile, + emitChangedFiles, + getChangedProgramFiles, + getSemanticDiagnostics, + clear + }; + + function createProgramGraph(program: Program, hasInvalidatedResolution: HasInvalidatedResolution) { + const currentIsModuleEmit = program.getCompilerOptions().module !== ModuleKind.None; + if (isModuleEmit !== currentIsModuleEmit) { + isModuleEmit = currentIsModuleEmit; + emitHandler = isModuleEmit ? getModuleEmitHandler() : getNonModuleEmitHandler(); + fileInfos.clear(); + semanticDiagnosticsPerFile.clear(); + } + mutateMap( + fileInfos, + arrayToMap(program.getSourceFiles(), sourceFile => sourceFile.path), + { + // Add new file info + createNewValue: (_path, sourceFile) => addNewFileInfo(program, sourceFile), + // Remove existing file info + onDeleteValue: removeExistingFileInfo, + // We will update in place instead of deleting existing value and adding new one + onExistingValue: (_key, existingInfo, sourceFile) => updateExistingFileInfo(program, existingInfo, sourceFile, hasInvalidatedResolution) + } + ); + } + + function registerChangedFile(path: Path, fileName: string) { + changedFileNames.set(path, fileName); + // All changed files need to re-evaluate its semantic diagnostics + semanticDiagnosticsPerFile.delete(path); + } + + function addNewFileInfo(program: Program, sourceFile: SourceFile): FileInfo { + registerChangedFile(sourceFile.path, sourceFile.fileName); + emitHandler.addScriptInfo(program, sourceFile); + return { fileName: sourceFile.fileName, version: sourceFile.version, signature: undefined }; + } + + function removeExistingFileInfo(path: Path, existingFileInfo: FileInfo) { + registerChangedFile(path, existingFileInfo.fileName); + emitHandler.removeScriptInfo(path); + } + + function updateExistingFileInfo(program: Program, existingInfo: FileInfo, sourceFile: SourceFile, hasInvalidatedResolution: HasInvalidatedResolution) { + if (existingInfo.version !== sourceFile.version || hasInvalidatedResolution(sourceFile.path)) { + registerChangedFile(sourceFile.path, sourceFile.fileName); + existingInfo.version = sourceFile.version; + emitHandler.updateScriptInfo(program, sourceFile); + } + } + + function ensureProgramGraph(program: Program) { + if (!emitHandler) { + createProgramGraph(program, returnFalse); + } + } + + function onProgramUpdateGraph(program: Program, hasInvalidatedResolution: HasInvalidatedResolution) { + if (emitHandler) { + createProgramGraph(program, hasInvalidatedResolution); + } + } + + function getFilesAffectedBy(program: Program, path: Path): string[] { + ensureProgramGraph(program); + + const sourceFile = program.getSourceFile(path); + const singleFileResult = sourceFile && shouldEmitFile(sourceFile) ? [sourceFile.fileName] : []; + const info = fileInfos.get(path); + if (!info || !updateShapeSignature(program, sourceFile, info)) { + return singleFileResult; + } + + Debug.assert(!!sourceFile); + return emitHandler.getFilesAffectedByUpdatedShape(program, sourceFile, singleFileResult); + } + + function emitFile(program: Program, path: Path) { + ensureProgramGraph(program); + if (!fileInfos.has(path)) { + return { outputFiles: [], emitSkipped: true }; + } + + return getEmitOutput(program, program.getSourceFileByPath(path), /*emitOnlyDtsFiles*/ false, /*isDetailed*/ false); + } + + function enumerateChangedFilesSet( + program: Program, + onChangedFile: (fileName: string, path: Path) => void, + onAffectedFile: (fileName: string, sourceFile: SourceFile) => void + ) { + changedFileNames.forEach((fileName, path) => { + onChangedFile(fileName, path as Path); + const affectedFiles = getFilesAffectedBy(program, path as Path); + for (const file of affectedFiles) { + onAffectedFile(file, program.getSourceFile(file)); + } + }); + } + + function enumerateChangedFilesEmitOutput( + program: Program, + emitOnlyDtsFiles: boolean, + onChangedFile: (fileName: string, path: Path) => void, + onEmitOutput: (emitOutput: EmitOutputDetailed, sourceFile: SourceFile) => void + ) { + const seenFiles = createMap(); + enumerateChangedFilesSet(program, onChangedFile, (fileName, sourceFile) => { + if (!seenFiles.has(fileName)) { + seenFiles.set(fileName, sourceFile); + if (sourceFile) { + // Any affected file shouldnt have the cached diagnostics + semanticDiagnosticsPerFile.delete(sourceFile.path); + + const emitOutput = getEmitOutput(program, sourceFile, emitOnlyDtsFiles, /*isDetailed*/ true) as EmitOutputDetailed; + onEmitOutput(emitOutput, sourceFile); + + // mark all the emitted source files as seen + if (emitOutput.emittedSourceFiles) { + for (const file of emitOutput.emittedSourceFiles) { + seenFiles.set(file.fileName, file); + } + } + } + } + }); + } + + function emitChangedFiles(program: Program): EmitOutputDetailed[] { + ensureProgramGraph(program); + const result: EmitOutputDetailed[] = []; + enumerateChangedFilesEmitOutput(program, /*emitOnlyDtsFiles*/ false, + /*onChangedFile*/ noop, emitOutput => result.push(emitOutput)); + changedFileNames.clear(); + return result; + } + + function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): Diagnostic[] { + ensureProgramGraph(program); + + // Ensure that changed files have cleared their respective + enumerateChangedFilesSet(program, /*onChangedFile*/ noop, (_affectedFileName, sourceFile) => { + if (sourceFile) { + semanticDiagnosticsPerFile.delete(sourceFile.path); + } + }); + + let diagnostics: Diagnostic[]; + for (const sourceFile of program.getSourceFiles()) { + const path = sourceFile.path; + const cachedDiagnostics = semanticDiagnosticsPerFile.get(path); + // Report the semantic diagnostics from the cache if we already have those diagnostics present + if (cachedDiagnostics) { + diagnostics = concatenate(diagnostics, cachedDiagnostics); + } + else { + // Diagnostics werent cached, get them from program, and cache the result + const cachedDiagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken); + semanticDiagnosticsPerFile.set(path, cachedDiagnostics); + diagnostics = concatenate(diagnostics, cachedDiagnostics); + } + } + return diagnostics || emptyArray; + } + + function getChangedProgramFiles(program: Program): ChangedProgramFiles { + ensureProgramGraph(program); + + let filesToEmit: string[]; + const changedFiles = createMap(); + enumerateChangedFilesEmitOutput(program, /*emitOnlyDtsFiles*/ true, + // All the changed files are required to get diagnostics + (changedFileName, changedFilePath) => addFileForDiagnostics(changedFileName, changedFilePath), + // Emitted file is for emit as well as diagnostic + (_emitOutput, sourceFile) => { + (filesToEmit || (filesToEmit = [])).push(sourceFile.fileName); + addFileForDiagnostics(sourceFile.fileName, sourceFile.path); + }); + changedFileNames.clear(); + return { + filesToEmit: filesToEmit || emptyArray, + changedFiles: arrayFrom(changedFiles.values()) + }; + + function addFileForDiagnostics(fileName: string, path: Path) { + changedFiles.set(path, fileName); + } + } + + function clear() { + isModuleEmit = undefined; + emitHandler = undefined; + fileInfos.clear(); + semanticDiagnosticsPerFile.clear(); + changedFileNames.clear(); + } + + /** + * For script files that contains only ambient external modules, although they are not actually external module files, + * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, + * there are no point to rebuild all script files if these special files have changed. However, if any statement + * in the file is not ambient external module, we treat it as a regular script file. + */ + function containsOnlyAmbientModules(sourceFile: SourceFile) { + for (const statement of sourceFile.statements) { + if (!isModuleWithStringLiteralName(statement)) { + return false; + } + } + return true; + } + + /** + * @return {boolean} indicates if the shape signature has changed since last update. + */ + function updateShapeSignature(program: Program, sourceFile: SourceFile, info: FileInfo) { + const prevSignature = info.signature; + let latestSignature: string; + if (sourceFile.isDeclarationFile) { + latestSignature = computeHash(sourceFile.text); + info.signature = latestSignature; + } + else { + const emitOutput = getEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, /*isDetailed*/ false); + if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { + latestSignature = computeHash(emitOutput.outputFiles[0].text); + info.signature = latestSignature; + } + else { + latestSignature = prevSignature; + } + } + + return !prevSignature || latestSignature !== prevSignature; + } + + /** + * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true + */ + function getReferencedFiles(program: Program, sourceFile: SourceFile): Map { + const referencedFiles = createMap(); + + // We need to use a set here since the code can contain the same import twice, + // but that will only be one dependency. + // To avoid invernal conversion, the key of the referencedFiles map must be of type Path + if (sourceFile.imports && sourceFile.imports.length > 0) { + const checker: TypeChecker = program.getTypeChecker(); + for (const importName of sourceFile.imports) { + const symbol = checker.getSymbolAtLocation(importName); + if (symbol && symbol.declarations && symbol.declarations[0]) { + const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]); + if (declarationSourceFile) { + referencedFiles.set(declarationSourceFile.path, true); + } + } + } + } + + const sourceFileDirectory = getDirectoryPath(sourceFile.path); + // Handle triple slash references + if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { + for (const referencedFile of sourceFile.referencedFiles) { + const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName); + referencedFiles.set(referencedPath, true); + } + } + + // Handle type reference directives + if (sourceFile.resolvedTypeReferenceDirectiveNames) { + sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { + if (!resolvedTypeReferenceDirective) { + return; + } + + const fileName = resolvedTypeReferenceDirective.resolvedFileName; + const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName); + referencedFiles.set(typeFilePath, true); + }); + } + + return referencedFiles; + } + + /** + * Gets all the emittable files from the program + */ + function getAllEmittableFiles(program: Program) { + const defaultLibraryFileName = getDefaultLibFileName(program.getCompilerOptions()); + const sourceFiles = program.getSourceFiles(); + const result: string[] = []; + for (const sourceFile of sourceFiles) { + if (getBaseFileName(sourceFile.fileName) !== defaultLibraryFileName && shouldEmitFile(sourceFile)) { + result.push(sourceFile.fileName); + } + } + return result; + } + + function getNonModuleEmitHandler(): EmitHandler { + return { + addScriptInfo: noop, + removeScriptInfo: noop, + updateScriptInfo: noop, + getFilesAffectedByUpdatedShape + }; + + function getFilesAffectedByUpdatedShape(program: Program, _sourceFile: SourceFile, singleFileResult: string[]): string[] { + const options = program.getCompilerOptions(); + // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, + // so returning the file itself is good enough. + if (options && (options.out || options.outFile)) { + return singleFileResult; + } + return getAllEmittableFiles(program); + } + } + + function getModuleEmitHandler(): EmitHandler { + const references = createMap>(); + return { + addScriptInfo: setReferences, + removeScriptInfo, + updateScriptInfo: setReferences, + getFilesAffectedByUpdatedShape + }; + + function setReferences(program: Program, sourceFile: SourceFile) { + references.set(sourceFile.path, getReferencedFiles(program, sourceFile)); + } + + function removeScriptInfo(removedFilePath: Path) { + // Remove existing references + references.forEach((referencesInFile, filePath) => { + if (referencesInFile.has(removedFilePath)) { + // add files referencing the removedFilePath, as changed files too + const referencedByInfo = fileInfos.get(filePath); + if (referencedByInfo) { + registerChangedFile(filePath as Path, referencedByInfo.fileName); + } + } + }); + // Delete the entry for the removed file path + references.delete(removedFilePath); + } + + function getReferencedByPaths(referencedFilePath: Path) { + return mapDefinedIter(references.entries(), ([filePath, referencesInFile]) => + referencesInFile.has(referencedFilePath) ? filePath as Path : undefined + ); + } + + function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile, singleFileResult: string[]): string[] { + if (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile)) { + return getAllEmittableFiles(program); + } + + const options = program.getCompilerOptions(); + if (options && (options.isolatedModules || options.out || options.outFile)) { + return singleFileResult; + } + + // Now we need to if each file in the referencedBy list has a shape change as well. + // Because if so, its own referencedBy files need to be saved as well to make the + // emitting result consistent with files on disk. + + const seenFileNamesMap = createMap(); + const setSeenFileName = (path: Path, sourceFile: SourceFile) => { + seenFileNamesMap.set(path, sourceFile && shouldEmitFile(sourceFile) ? sourceFile.fileName : undefined); + }; + + // Start with the paths this file was referenced by + const path = sourceFile.path; + setSeenFileName(path, sourceFile); + const queue = getReferencedByPaths(path); + while (queue.length > 0) { + const currentPath = queue.pop(); + if (!seenFileNamesMap.has(currentPath)) { + const currentSourceFile = program.getSourceFileByPath(currentPath); + if (currentSourceFile && updateShapeSignature(program, currentSourceFile, fileInfos.get(currentPath))) { + queue.push(...getReferencedByPaths(currentPath)); + } + setSeenFileName(currentPath, currentSourceFile); + } + } + + // Return array of values that needs emit + return arrayFrom(seenFileNamesMap.values()); + } + } + } +} diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6df6b1f9ce..550a8edfde 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1166,7 +1166,7 @@ namespace ts { } function diagnosticName(nameArg: __String | Identifier) { - return typeof nameArg === "string" ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier); + return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier); } function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 113e8786e6..12ef0c082c 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -885,7 +885,7 @@ namespace ts { */ export function readConfigFile(fileName: string, readFile: (path: string) => string | undefined): { config?: any; error?: Diagnostic } { const textOrDiagnostic = tryReadFile(fileName, readFile); - return typeof textOrDiagnostic === "string" ? parseConfigFileTextToJson(fileName, textOrDiagnostic) : { config: {}, error: textOrDiagnostic }; + return isString(textOrDiagnostic) ? parseConfigFileTextToJson(fileName, textOrDiagnostic) : { config: {}, error: textOrDiagnostic }; } /** @@ -907,7 +907,7 @@ namespace ts { */ export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): JsonSourceFile { const textOrDiagnostic = tryReadFile(fileName, readFile); - return typeof textOrDiagnostic === "string" ? parseJsonText(fileName, textOrDiagnostic) : { parseDiagnostics: [textOrDiagnostic] }; + return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { parseDiagnostics: [textOrDiagnostic] }; } function tryReadFile(fileName: string, readFile: (path: string) => string | undefined): string | Diagnostic { @@ -1111,9 +1111,9 @@ namespace ts { if (!isDoubleQuotedString(valueExpression)) { errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.String_literal_with_double_quotes_expected)); } - reportInvalidOptionValue(option && (typeof option.type === "string" && option.type !== "string")); + reportInvalidOptionValue(option && (isString(option.type) && option.type !== "string")); const text = (valueExpression).text; - if (option && typeof option.type !== "string") { + if (option && !isString(option.type)) { const customOption = option; // Validate custom option type if (!customOption.type.has(text.toLowerCase())) { @@ -1184,7 +1184,7 @@ namespace ts { function getCompilerOptionValueTypeString(option: CommandLineOption) { return option.type === "list" ? "Array" : - typeof option.type === "string" ? option.type : "string"; + isString(option.type) ? option.type : "string"; } function isCompilerOptionsValue(option: CommandLineOption, value: any): value is CompilerOptionsValue { @@ -1192,7 +1192,7 @@ namespace ts { if (option.type === "list") { return isArray(value); } - const expectedType = typeof option.type === "string" ? option.type : "string"; + const expectedType = isString(option.type) ? option.type : "string"; return typeof value === expectedType; } } @@ -1578,7 +1578,7 @@ namespace ts { let extendedConfigPath: Path; if (json.extends) { - if (typeof json.extends !== "string") { + if (!isString(json.extends)) { errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string")); } else { @@ -1803,7 +1803,7 @@ namespace ts { if (optType === "list" && isArray(value)) { return convertJsonOptionOfListType(opt, value, basePath, errors); } - else if (typeof optType !== "string") { + else if (!isString(optType)) { return convertJsonOptionOfCustomType(opt, value, errors); } return normalizeNonListOptionValue(opt, basePath, value); @@ -1816,13 +1816,13 @@ namespace ts { function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue { if (option.type === "list") { const listOption = option; - if (listOption.element.isFilePath || typeof listOption.element.type !== "string") { + if (listOption.element.isFilePath || !isString(listOption.element.type)) { return filter(map(value, v => normalizeOptionValue(listOption.element, basePath, v)), v => !!v); } return value; } - else if (typeof option.type !== "string") { - return option.type.get(typeof value === "string" ? value.toLowerCase() : value); + else if (!isString(option.type)) { + return option.type.get(isString(value) ? value.toLowerCase() : value); } return normalizeNonListOptionValue(option, basePath, value); } @@ -1984,7 +1984,7 @@ namespace ts { * @param host The host used to resolve files and directories. * @param extraFileExtensions optionaly file extra file extension information from host */ - export function getFileNamesFromConfigSpecs(spec: ConfigFileSpecs, basePath: string, options: CompilerOptions, host: ParseConfigHost, extraFileExtensions: ReadonlyArray): ExpandResult { + export function getFileNamesFromConfigSpecs(spec: ConfigFileSpecs, basePath: string, options: CompilerOptions, host: ParseConfigHost, extraFileExtensions: ReadonlyArray = []): ExpandResult { basePath = normalizePath(basePath); const keyMapper = host.useCaseSensitiveFileNames ? caseSensitiveKeyMapper : caseInsensitiveKeyMapper; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 55e76a6c6f..00e8065a9f 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1202,6 +1202,13 @@ namespace ts { return Array.isArray ? Array.isArray(value) : value instanceof Array; } + /** + * Tests whether a value is string + */ + export function isString(text: any): text is string { + return typeof text === "string"; + } + export function tryCast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined { return value !== undefined && test(value) ? value : undefined; } @@ -1212,7 +1219,10 @@ namespace ts { } /** Does nothing. */ - export function noop(): void {} + export function noop(): void { } + + /** Do nothing and return false */ + export function returnFalse(): false { return false; } /** Throws an error because a function is not implemented. */ export function notImplemented(): never { @@ -1455,16 +1465,16 @@ namespace ts { function compareMessageText(text1: string | DiagnosticMessageChain, text2: string | DiagnosticMessageChain): Comparison { while (text1 && text2) { // We still have both chains. - const string1 = typeof text1 === "string" ? text1 : text1.messageText; - const string2 = typeof text2 === "string" ? text2 : text2.messageText; + const string1 = isString(text1) ? text1 : text1.messageText; + const string2 = isString(text2) ? text2 : text2.messageText; const res = compareValues(string1, string2); if (res) { return res; } - text1 = typeof text1 === "string" ? undefined : text1.next; - text2 = typeof text2 === "string" ? undefined : text2.next; + text1 = isString(text1) ? undefined : text1.next; + text2 = isString(text2) ? undefined : text2.next; } if (!text1 && !text2) { @@ -2066,8 +2076,8 @@ namespace ts { } export interface FileSystemEntries { - files: ReadonlyArray; - directories: ReadonlyArray; + readonly files: ReadonlyArray; + readonly directories: ReadonlyArray; } export interface FileMatcherPatterns { @@ -2620,4 +2630,204 @@ namespace ts { export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) { return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs; } + + export interface HostForCaching extends PartialSystem { + useCaseSensitiveFileNames: boolean; + } + + export interface CachedHost { + addOrDeleteFileOrFolder(fileOrFolder: string, fileOrFolderPath: Path): void; + addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void; + clearCache(): void; + } + + export interface CachedPartialSystem extends PartialSystem, CachedHost { + } + + interface MutableFileSystemEntries { + readonly files: string[]; + readonly directories: string[]; + } + + export function createCachedPartialSystem(host: HostForCaching): CachedPartialSystem { + const cachedReadDirectoryResult = createMap(); + const getCurrentDirectory = memoize(() => host.getCurrentDirectory()); + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); + return { + writeFile, + fileExists, + directoryExists, + createDirectory, + getCurrentDirectory, + getDirectories, + readDirectory, + addOrDeleteFileOrFolder, + addOrDeleteFile, + clearCache + }; + + function toPath(fileName: string) { + return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName); + } + + function getCachedFileSystemEntries(rootDirPath: Path): MutableFileSystemEntries | undefined { + return cachedReadDirectoryResult.get(rootDirPath); + } + + function getCachedFileSystemEntriesForBaseDir(path: Path): MutableFileSystemEntries | undefined { + return getCachedFileSystemEntries(getDirectoryPath(path)); + } + + function getBaseNameOfFileName(fileName: string) { + return getBaseFileName(normalizePath(fileName)); + } + + function createCachedFileSystemEntries(rootDir: string, rootDirPath: Path) { + const resultFromHost: MutableFileSystemEntries = { + files: map(host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]), getBaseNameOfFileName) || [], + directories: host.getDirectories(rootDir) || [] + }; + + cachedReadDirectoryResult.set(rootDirPath, resultFromHost); + return resultFromHost; + } + + /** + * If the readDirectory result was already cached, it returns that + * Otherwise gets result from host and caches it. + * The host request is done under try catch block to avoid caching incorrect result + */ + function tryReadDirectory(rootDir: string, rootDirPath: Path): MutableFileSystemEntries | undefined { + const cachedResult = getCachedFileSystemEntries(rootDirPath); + if (cachedResult) { + return cachedResult; + } + + try { + return createCachedFileSystemEntries(rootDir, rootDirPath); + } + catch (_e) { + // If there is exception to read directories, dont cache the result and direct the calls to host + Debug.assert(!cachedReadDirectoryResult.has(rootDirPath)); + return undefined; + } + } + + function fileNameEqual(name1: string, name2: string) { + return getCanonicalFileName(name1) === getCanonicalFileName(name2); + } + + function hasEntry(entries: ReadonlyArray, name: string) { + return some(entries, file => fileNameEqual(file, name)); + } + + function updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) { + if (hasEntry(entries, baseName)) { + if (!isValid) { + return filterMutate(entries, entry => !fileNameEqual(entry, baseName)); + } + } + else if (isValid) { + return entries.push(baseName); + } + } + + function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void { + const path = toPath(fileName); + const result = getCachedFileSystemEntriesForBaseDir(path); + if (result) { + updateFilesOfFileSystemEntry(result, getBaseNameOfFileName(fileName), /*fileExists*/ true); + } + return host.writeFile(fileName, data, writeByteOrderMark); + } + + function fileExists(fileName: string): boolean { + const path = toPath(fileName); + const result = getCachedFileSystemEntriesForBaseDir(path); + return result && hasEntry(result.files, getBaseNameOfFileName(fileName)) || + host.fileExists(fileName); + } + + function directoryExists(dirPath: string): boolean { + const path = toPath(dirPath); + return cachedReadDirectoryResult.has(path) || host.directoryExists(dirPath); + } + + function createDirectory(dirPath: string) { + const path = toPath(dirPath); + const result = getCachedFileSystemEntriesForBaseDir(path); + const baseFileName = getBaseNameOfFileName(dirPath); + if (result) { + updateFileSystemEntry(result.directories, baseFileName, /*isValid*/ true); + } + host.createDirectory(dirPath); + } + + function getDirectories(rootDir: string): string[] { + const rootDirPath = toPath(rootDir); + const result = tryReadDirectory(rootDir, rootDirPath); + if (result) { + return result.directories.slice(); + } + return host.getDirectories(rootDir); + } + + function readDirectory(rootDir: string, extensions?: ReadonlyArray, excludes?: ReadonlyArray, includes?: ReadonlyArray, depth?: number): string[] { + const rootDirPath = toPath(rootDir); + const result = tryReadDirectory(rootDir, rootDirPath); + if (result) { + return matchFiles(rootDir, extensions, excludes, includes, host.useCaseSensitiveFileNames, getCurrentDirectory(), depth, getFileSystemEntries); + } + return host.readDirectory(rootDir, extensions, excludes, includes, depth); + + function getFileSystemEntries(dir: string) { + const path = toPath(dir); + if (path === rootDirPath) { + return result; + } + return getCachedFileSystemEntries(path) || createCachedFileSystemEntries(dir, path); + } + } + + function addOrDeleteFileOrFolder(fileOrFolder: string, fileOrFolderPath: Path) { + const existingResult = getCachedFileSystemEntries(fileOrFolderPath); + if (existingResult) { + // This was a folder already present, remove it if this doesnt exist any more + if (!host.directoryExists(fileOrFolder)) { + cachedReadDirectoryResult.delete(fileOrFolderPath); + } + } + else { + // This was earlier a file (hence not in cached directory contents) + // or we never cached the directory containing it + const parentResult = getCachedFileSystemEntriesForBaseDir(fileOrFolderPath); + if (parentResult) { + const baseName = getBaseNameOfFileName(fileOrFolder); + if (parentResult) { + updateFilesOfFileSystemEntry(parentResult, baseName, host.fileExists(fileOrFolderPath)); + updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrFolderPath)); + } + } + } + } + + function addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind) { + if (eventKind === FileWatcherEventKind.Changed) { + return; + } + + const parentResult = getCachedFileSystemEntriesForBaseDir(filePath); + if (parentResult) { + updateFilesOfFileSystemEntry(parentResult, getBaseNameOfFileName(fileName), eventKind === FileWatcherEventKind.Created); + } + } + + function updateFilesOfFileSystemEntry(parentResult: MutableFileSystemEntries, baseName: string, fileExists: boolean) { + updateFileSystemEntry(parentResult.files, baseName, fileExists); + } + + function clearCache() { + cachedReadDirectoryResult.clear(); + } + } } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index daec1bce1e..82d194f3cf 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -81,7 +81,7 @@ namespace ts { if (typeof value === "boolean") { return value ? createTrue() : createFalse(); } - if (typeof value === "string") { + if (isString(value)) { return createStringLiteral(value); } return createLiteralFromNode(value); @@ -2130,7 +2130,7 @@ namespace ts { export function createCatchClause(variableDeclaration: string | VariableDeclaration | undefined, block: Block) { const node = createSynthesizedNode(SyntaxKind.CatchClause); - node.variableDeclaration = typeof variableDeclaration === "string" ? createVariableDeclaration(variableDeclaration) : variableDeclaration; + node.variableDeclaration = isString(variableDeclaration) ? createVariableDeclaration(variableDeclaration) : variableDeclaration; node.block = block; return node; } @@ -2438,11 +2438,11 @@ namespace ts { function asName(name: string | EntityName): EntityName; function asName(name: string | Identifier | ThisTypeNode): Identifier | ThisTypeNode; function asName(name: string | Identifier | BindingName | PropertyName | QualifiedName | ThisTypeNode) { - return typeof name === "string" ? createIdentifier(name) : name; + return isString(name) ? createIdentifier(name) : name; } function asExpression(value: string | number | Expression) { - return typeof value === "string" || typeof value === "number" ? createLiteral(value) : value; + return isString(value) || typeof value === "number" ? createLiteral(value) : value; } function asNodeArray(array: ReadonlyArray | undefined): NodeArray | undefined { diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 5fdac50489..4d4fc6c1d8 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -94,7 +94,7 @@ namespace ts { } const fileName = jsonContent[fieldName]; - if (typeof fileName !== "string") { + if (!isString(fileName)) { if (state.traceEnabled) { trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof fileName); } @@ -659,8 +659,8 @@ namespace ts { } if (matchedPattern) { - const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName); - const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern); + const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName); + const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern); if (state.traceEnabled) { trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 305058285f..c3b83e1ba7 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1,6 +1,7 @@ /// /// /// +/// namespace ts { const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/; @@ -227,19 +228,25 @@ namespace ts { let output = ""; for (const diagnostic of diagnostics) { - if (diagnostic.file) { - const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); - const fileName = diagnostic.file.fileName; - const relativeFileName = convertToRelativePath(fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)); - output += `${relativeFileName}(${line + 1},${character + 1}): `; - } - - const category = DiagnosticCategory[diagnostic.category].toLowerCase(); - output += `${category} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`; + output += formatDiagnostic(diagnostic, host); } return output; } + export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string { + const category = DiagnosticCategory[diagnostic.category].toLowerCase(); + const errorMessage = `${category} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`; + + if (diagnostic.file) { + const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); + const fileName = diagnostic.file.fileName; + const relativeFileName = convertToRelativePath(fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)); + return `${relativeFileName}(${line + 1},${character + 1}): ` + errorMessage; + } + + return errorMessage; + } + const redForegroundEscapeSequence = "\u001b[91m"; const yellowForegroundEscapeSequence = "\u001b[93m"; const blueForegroundEscapeSequence = "\u001b[93m"; @@ -337,7 +344,7 @@ namespace ts { } export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string { - if (typeof messageText === "string") { + if (isString(messageText)) { return messageText; } else { @@ -386,6 +393,142 @@ namespace ts { allDiagnostics?: Diagnostic[]; } + export function isProgramUptoDate(program: Program | undefined, rootFileNames: string[], newOptions: CompilerOptions, + getSourceVersion: (path: Path) => string, fileExists: (fileName: string) => boolean, hasInvalidatedResolution: HasInvalidatedResolution): boolean { + // If we haven't create a program yet, then it is not up-to-date + if (!program) { + return false; + } + + // If number of files in the program do not match, it is not up-to-date + if (program.getRootFileNames().length !== rootFileNames.length) { + return false; + } + + // If any file is not up-to-date, then the whole program is not up-to-date + if (program.getSourceFiles().some(sourceFileNotUptoDate)) { + return false; + } + + // If any of the missing file paths are now created + if (program.getMissingFilePaths().some(fileExists)) { + return false; + } + + const currentOptions = program.getCompilerOptions(); + // If the compilation settings do no match, then the program is not up-to-date + if (!compareDataObjects(currentOptions, newOptions)) { + return false; + } + + // If everything matches but the text of config file is changed, + // error locations can change for program options, so update the program + if (currentOptions.configFile && newOptions.configFile) { + return currentOptions.configFile.text === newOptions.configFile.text; + } + + return true; + + function sourceFileNotUptoDate(sourceFile: SourceFile): boolean { + return sourceFile.version !== getSourceVersion(sourceFile.path) || + hasInvalidatedResolution(sourceFile.path); + } + } + + /** + * Determined if source file needs to be re-created even if its text hasnt changed + */ + function shouldProgramCreateNewSourceFiles(program: Program, newOptions: CompilerOptions) { + // If any of these options change, we cant reuse old source file even if version match + // The change in options like these could result in change in syntax tree change + const oldOptions = program && program.getCompilerOptions(); + return oldOptions && + (oldOptions.target !== newOptions.target || + oldOptions.module !== newOptions.module || + oldOptions.moduleResolution !== newOptions.moduleResolution || + oldOptions.noResolve !== newOptions.noResolve || + oldOptions.jsx !== newOptions.jsx || + oldOptions.allowJs !== newOptions.allowJs || + oldOptions.disableSizeLimit !== newOptions.disableSizeLimit || + oldOptions.baseUrl !== newOptions.baseUrl || + !equalOwnProperties(oldOptions.paths, newOptions.paths)); + } + + /** + * Updates the existing missing file watches with the new set of missing files after new program is created + */ + export function updateMissingFilePathsWatch( + program: Program, + missingFileWatches: Map, + createMissingFileWatch: (missingFilePath: Path) => FileWatcher, + closeExistingMissingFilePathFileWatcher: (missingFilePath: Path, fileWatcher: FileWatcher) => void + ) { + const missingFilePaths = program.getMissingFilePaths(); + const newMissingFilePathMap = arrayToSet(missingFilePaths); + // Update the missing file paths watcher + mutateMap( + missingFileWatches, + newMissingFilePathMap, + { + // Watch the missing files + createNewValue: createMissingFileWatch, + // Files that are no longer missing (e.g. because they are no longer required) + // should no longer be watched. + onDeleteValue: closeExistingMissingFilePathFileWatcher + } + ); + } + + export interface WildcardDirectoryWatcher { + watcher: FileWatcher; + flags: WatchDirectoryFlags; + } + + /** + * Updates the existing wild card directory watches with the new set of wild card directories from the config file + * after new program is created because the config file was reloaded or program was created first time from the config file + * Note that there is no need to call this function when the program is updated with additional files without reloading config files, + * as wildcard directories wont change unless reloading config file + */ + export function updateWatchingWildcardDirectories( + existingWatchedForWildcards: Map, + wildcardDirectories: Map, + watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher, + closeDirectoryWatcher: (directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatcher, flagsChanged: boolean) => void + ) { + mutateMap( + existingWatchedForWildcards, + wildcardDirectories, + { + // Create new watch and recursive info + createNewValue: createWildcardDirectoryWatcher, + // Close existing watch thats not needed any more + onDeleteValue: (directory, wildcardDirectoryWatcher) => + closeDirectoryWatcher(directory, wildcardDirectoryWatcher, /*flagsChanged*/ false), + // Close existing watch that doesnt match in the flags + onExistingValue: updateWildcardDirectoryWatcher + } + ); + + function createWildcardDirectoryWatcher(directory: string, flags: WatchDirectoryFlags): WildcardDirectoryWatcher { + // Create new watch and recursive info + return { + watcher: watchDirectory(directory, flags), + flags + }; + } + + function updateWildcardDirectoryWatcher(directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatcher, flags: WatchDirectoryFlags) { + // Watcher needs to be updated if the recursive flags dont match + if (wildcardDirectoryWatcher.flags === flags) { + return; + } + + closeDirectoryWatcher(directory, wildcardDirectoryWatcher, /*flagsChanged*/ true); + existingWatchedForWildcards.set(directory, createWildcardDirectoryWatcher(directory, flags)); + } + } + /** * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions' * that represent a compilation unit. @@ -446,6 +589,7 @@ namespace ts { let moduleResolutionCache: ModuleResolutionCache; let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[]; + const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse; if (host.resolveModuleNames) { resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(checkAllDefined(moduleNames), containingFile).map(resolved => { // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName. @@ -482,10 +626,12 @@ namespace ts { let redirectTargetsSet = createMap(); const filesByName = createMap(); + let missingFilePaths: ReadonlyArray; // stores 'filename -> file association' ignoring case // used to track cases when two file names differ only in casing const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap() : undefined; + const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); const structuralIsReused = tryReuseStructureFromOldProgram(); if (structuralIsReused !== StructureIsReused.Completely) { forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false)); @@ -520,13 +666,26 @@ namespace ts { }); } } + + missingFilePaths = arrayFrom(filesByName.keys(), p => p).filter(p => !filesByName.get(p)); } - const missingFilePaths = arrayFrom(filesByName.keys(), p => p).filter(p => !filesByName.get(p)); + Debug.assert(!!missingFilePaths); // unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks moduleResolutionCache = undefined; + // Release any files we have acquired in the old program but are + // not part of the new program. + if (oldProgram && host.onReleaseOldSourceFile) { + const oldSourceFiles = oldProgram.getSourceFiles(); + for (const oldSourceFile of oldSourceFiles) { + if (!getSourceFile(oldSourceFile.path) || shouldCreateNewSourceFile) { + host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions()); + } + } + } + // unconditionally set oldProgram to undefined to prevent it from being captured in closure oldProgram = undefined; @@ -682,7 +841,7 @@ namespace ts { trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, containingFile); } } - else { + else if (!hasInvalidatedResolution(oldProgramState.file.path)) { resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, oldProgramState); } @@ -784,14 +943,21 @@ namespace ts { const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = []; oldProgram.structureIsReused = StructureIsReused.Completely; + // If the missing file paths are now present, it can change the progam structure, + // and hence cant reuse the structure. + // This is same as how we dont reuse the structure if one of the file from old program is now missing + if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + const oldSourceFiles = oldProgram.getSourceFiles(); const enum SeenPackageName { Exists, Modified } const seenPackageNames = createMap(); for (const oldSourceFile of oldSourceFiles) { let newSourceFile = host.getSourceFileByPath - ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target) - : host.getSourceFile(oldSourceFile.fileName, options.target); + ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target, /*onError*/ undefined, shouldCreateNewSourceFile) + : host.getSourceFile(oldSourceFile.fileName, options.target, /*onError*/ undefined, shouldCreateNewSourceFile); if (!newSourceFile) { return oldProgram.structureIsReused = StructureIsReused.Not; @@ -874,6 +1040,13 @@ namespace ts { // tentatively approve the file modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile }); } + else if (hasInvalidatedResolution(oldSourceFile.path)) { + // 'module/types' references could have changed + oldProgram.structureIsReused = StructureIsReused.SafeModules; + + // add file to the modified list so that we will resolve it later + modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile }); + } // if file has passed all checks it should be safe to reuse it newSourceFiles.push(newSourceFile); @@ -920,20 +1093,7 @@ namespace ts { return oldProgram.structureIsReused; } - // If a file has ceased to be missing, then we need to discard some of the old - // structure in order to pick it up. - // Caution: if the file has created and then deleted between since it was discovered to - // be missing, then the corresponding file watcher will have been closed and no new one - // will be created until we encounter a change that prevents complete structure reuse. - // During this interval, creation of the file will go unnoticed. We expect this to be - // both rare and low-impact. - if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) { - return oldProgram.structureIsReused = StructureIsReused.SafeModules; - } - - for (const p of oldProgram.getMissingFilePaths()) { - filesByName.set(p, undefined); - } + missingFilePaths = oldProgram.getMissingFilePaths(); // update fileName -> file mapping for (let i = 0; i < newSourceFiles.length; i++) { @@ -1670,7 +1830,7 @@ namespace ts { else { fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); } - }); + }, shouldCreateNewSourceFile); if (packageId) { const packageIdKey = `${packageId.name}@${packageId.version}`; diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts new file mode 100644 index 0000000000..6d7c1f6c0e --- /dev/null +++ b/src/compiler/resolutionCache.ts @@ -0,0 +1,304 @@ +/// +/// + +/*@internal*/ +namespace ts { + /** This is the cache of module/typedirectives resolution that can be retained across program */ + export interface ResolutionCache { + setModuleResolutionHost(host: ModuleResolutionHost): void; + + startRecordingFilesWithChangedResolutions(): void; + finishRecordingFilesWithChangedResolutions(): Path[]; + + resolveModuleNames(moduleNames: string[], containingFile: string, logChanges: boolean): ResolvedModuleFull[]; + resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + + invalidateResolutionOfFile(filePath: Path): void; + invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath: Path): void; + + createHasInvalidatedResolution(): HasInvalidatedResolution; + + clear(): void; + } + + interface NameResolutionWithFailedLookupLocations { + readonly failedLookupLocations: ReadonlyArray; + isInvalidated?: boolean; + } + + interface FailedLookupLocationsWatcher { + fileWatcher: FileWatcher; + refCount: number; + } + + export function createResolutionCache( + toPath: (fileName: string) => Path, + getCompilerOptions: () => CompilerOptions, + watchForFailedLookupLocation: (failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) => FileWatcher, + log: (s: string) => void, + projectName?: string, + getGlobalCache?: () => string | undefined): ResolutionCache { + + let host: ModuleResolutionHost; + let filesWithChangedSetOfUnresolvedImports: Path[] | undefined; + let filesWithInvalidatedResolutions: Map | undefined; + + // The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file. + // The key in the map is source file's path. + // The values are Map of resolutions with key being name lookedup. + const resolvedModuleNames = createMap>(); + const resolvedTypeReferenceDirectives = createMap>(); + + const failedLookupLocationsWatches = createMap(); + + return { + setModuleResolutionHost, + startRecordingFilesWithChangedResolutions, + finishRecordingFilesWithChangedResolutions, + resolveModuleNames, + resolveTypeReferenceDirectives, + invalidateResolutionOfFile, + invalidateResolutionOfChangedFailedLookupLocation, + createHasInvalidatedResolution, + clear + }; + + function setModuleResolutionHost(updatedHost: ModuleResolutionHost) { + host = updatedHost; + } + + function clear() { + // Close all the watches for failed lookup locations, irrespective of refcounts for them since this is to clear the cache + clearMap(failedLookupLocationsWatches, (failedLookupLocationPath, failedLookupLocationWatcher) => { + log(`Watcher: FailedLookupLocations: Status: ForceClose: LocationPath: ${failedLookupLocationPath}, refCount: ${failedLookupLocationWatcher.refCount}`); + failedLookupLocationWatcher.fileWatcher.close(); + }); + resolvedModuleNames.clear(); + resolvedTypeReferenceDirectives.clear(); + } + + function startRecordingFilesWithChangedResolutions() { + filesWithChangedSetOfUnresolvedImports = []; + } + + function finishRecordingFilesWithChangedResolutions() { + const collected = filesWithChangedSetOfUnresolvedImports; + filesWithChangedSetOfUnresolvedImports = undefined; + return collected; + } + + function createHasInvalidatedResolution(): HasInvalidatedResolution { + const collected = filesWithInvalidatedResolutions; + filesWithInvalidatedResolutions = undefined; + return path => collected && collected.has(path); + } + + function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { + const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host); + // return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts + if (!getGlobalCache) { + return primaryResult; + } + + // otherwise try to load typings from @types + const globalCache = getGlobalCache(); + if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension))) { + // create different collection of failed lookup locations for second pass + // if it will fail and we've already found something during the first pass - we don't want to pollute its results + const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, projectName, compilerOptions, host, globalCache); + if (resolvedModule) { + return { resolvedModule, failedLookupLocations: addRange(primaryResult.failedLookupLocations as Array, failedLookupLocations) }; + } + } + + // Default return the result from the first pass + return primaryResult; + } + + function resolveNamesWithLocalCache( + names: string[], + containingFile: string, + cache: Map>, + loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T, + getResult: (s: T) => R, + getResultFileName: (result: R) => string | undefined, + logChanges: boolean): R[] { + + const path = toPath(containingFile); + const currentResolutionsInFile = cache.get(path); + + const newResolutions: Map = createMap(); + const resolvedModules: R[] = []; + const compilerOptions = getCompilerOptions(); + + for (const name of names) { + // check if this is a duplicate entry in the list + let resolution = newResolutions.get(name); + if (!resolution) { + const existingResolution = currentResolutionsInFile && currentResolutionsInFile.get(name); + if (moduleResolutionIsValid(existingResolution)) { + // ok, it is safe to use existing name resolution results + resolution = existingResolution; + } + else { + resolution = loader(name, containingFile, compilerOptions, host); + updateFailedLookupLocationWatches(containingFile, name, existingResolution && existingResolution.failedLookupLocations, resolution.failedLookupLocations); + } + newResolutions.set(name, resolution); + if (logChanges && filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) { + filesWithChangedSetOfUnresolvedImports.push(path); + // reset log changes to avoid recording the same file multiple times + logChanges = false; + } + } + + Debug.assert(resolution !== undefined); + + resolvedModules.push(getResult(resolution)); + } + + // replace old results with a new one + cache.set(path, newResolutions); + return resolvedModules; + + function resolutionIsEqualTo(oldResolution: T, newResolution: T): boolean { + if (oldResolution === newResolution) { + return true; + } + if (!oldResolution || !newResolution || oldResolution.isInvalidated) { + return false; + } + const oldResult = getResult(oldResolution); + const newResult = getResult(newResolution); + if (oldResult === newResult) { + return true; + } + if (!oldResult || !newResult) { + return false; + } + return getResultFileName(oldResult) === getResultFileName(newResult); + } + + function moduleResolutionIsValid(resolution: T): boolean { + if (!resolution || resolution.isInvalidated) { + return false; + } + + const result = getResult(resolution); + if (result) { + return true; + } + + // consider situation if we have no candidate locations as valid resolution. + // after all there is no point to invalidate it if we have no idea where to look for the module. + return resolution.failedLookupLocations.length === 0; + } + } + + function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] { + return resolveNamesWithLocalCache(typeDirectiveNames, containingFile, resolvedTypeReferenceDirectives, resolveTypeReferenceDirective, + m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName, /*logChanges*/ false); + } + + function resolveModuleNames(moduleNames: string[], containingFile: string, logChanges: boolean): ResolvedModuleFull[] { + return resolveNamesWithLocalCache(moduleNames, containingFile, resolvedModuleNames, resolveModuleName, + m => m.resolvedModule, r => r.resolvedFileName, logChanges); + } + + function watchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) { + const failedLookupLocationWatcher = failedLookupLocationsWatches.get(failedLookupLocationPath); + if (failedLookupLocationWatcher) { + failedLookupLocationWatcher.refCount++; + log(`Watcher: FailedLookupLocations: Status: Using existing watcher: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name} refCount: ${failedLookupLocationWatcher.refCount}`); + } + else { + log(`Watcher: FailedLookupLocations: Status: new watch: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name}`); + const fileWatcher = watchForFailedLookupLocation(failedLookupLocation, failedLookupLocationPath, containingFile, name); + failedLookupLocationsWatches.set(failedLookupLocationPath, { fileWatcher, refCount: 1 }); + } + } + + function closeFailedLookupLocationWatcher(failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) { + const failedLookupLocationWatcher = failedLookupLocationsWatches.get(failedLookupLocationPath); + Debug.assert(!!failedLookupLocationWatcher); + failedLookupLocationWatcher.refCount--; + if (failedLookupLocationWatcher.refCount) { + log(`Watcher: FailedLookupLocations: Status: Removing existing watcher: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name}: refCount: ${failedLookupLocationWatcher.refCount}`); + } + else { + log(`Watcher: FailedLookupLocations: Status: Closing the file watcher: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name}`); + failedLookupLocationWatcher.fileWatcher.close(); + failedLookupLocationsWatches.delete(failedLookupLocationPath); + } + } + + type FailedLookupLocationAction = (failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) => void; + function withFailedLookupLocations(failedLookupLocations: ReadonlyArray, containingFile: string, name: string, fn: FailedLookupLocationAction) { + forEach(failedLookupLocations, failedLookupLocation => { + fn(failedLookupLocation, toPath(failedLookupLocation), containingFile, name); + }); + } + + function updateFailedLookupLocationWatches(containingFile: string, name: string, existingFailedLookupLocations: ReadonlyArray | undefined, failedLookupLocations: ReadonlyArray) { + // Watch all the failed lookup locations + withFailedLookupLocations(failedLookupLocations, containingFile, name, watchFailedLookupLocation); + + // Close existing watches for the failed locations + withFailedLookupLocations(existingFailedLookupLocations, containingFile, name, closeFailedLookupLocationWatcher); + } + + function invalidateResolutionCacheOfDeletedFile( + deletedFilePath: Path, + cache: Map>, + getResult: (s: T) => R, + getResultFileName: (result: R) => string | undefined) { + cache.forEach((value, path) => { + if (path === deletedFilePath) { + cache.delete(path); + value.forEach((resolution, name) => { + withFailedLookupLocations(resolution.failedLookupLocations, path, name, closeFailedLookupLocationWatcher); + }); + } + else if (value) { + value.forEach(resolution => { + if (resolution && !resolution.isInvalidated) { + const result = getResult(resolution); + if (result) { + if (toPath(getResultFileName(result)) === deletedFilePath) { + resolution.isInvalidated = true; + (filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap())).set(path, true); + } + } + } + }); + } + }); + } + + function invalidateResolutionCacheOfChangedFailedLookupLocation( + failedLookupLocationPath: Path, + cache: Map>) { + cache.forEach((value, containingFile) => { + if (value) { + value.forEach(resolution => { + if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, location => toPath(location) === failedLookupLocationPath)) { + // Mark the file as needing re-evaluation of module resolution instead of using it blindly. + resolution.isInvalidated = true; + (filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap())).set(containingFile, true); + } + }); + } + }); + } + + function invalidateResolutionOfFile(filePath: Path) { + invalidateResolutionCacheOfDeletedFile(filePath, resolvedModuleNames, m => m.resolvedModule, r => r.resolvedFileName); + invalidateResolutionCacheOfDeletedFile(filePath, resolvedTypeReferenceDirectives, m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName); + } + + function invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath: Path) { + invalidateResolutionCacheOfChangedFailedLookupLocation(failedLookupLocationPath, resolvedModuleNames); + invalidateResolutionCacheOfChangedFailedLookupLocation(failedLookupLocationPath, resolvedTypeReferenceDirectives); + } + } +} diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 8a4c4ad22c..982d679381 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -30,14 +30,26 @@ namespace ts { mtime?: Date; } - export interface System { + /** + * Partial interface of the System thats needed to support the caching of directory structure + */ + export interface PartialSystem { + writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + createDirectory(path: string): void; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + } + + export interface System extends PartialSystem { args: string[]; newLine: string; useCaseSensitiveFileNames: boolean; write(s: string): void; readFile(path: string, encoding?: string): string | undefined; getFileSize?(path: string): number; - writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching @@ -45,13 +57,7 @@ namespace ts { watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; - fileExists(path: string): boolean; - directoryExists(path: string): boolean; - createDirectory(path: string): void; getExecutingFilePath(): string; - getCurrentDirectory(): string; - getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -184,7 +190,7 @@ namespace ts { function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) { // When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined" - const fileName = typeof relativeFileName !== "string" + const fileName = !isString(relativeFileName) ? undefined : ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath); // Some applications save a working file via rename operations diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index db25afe45b..78586e5479 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -1,48 +1,13 @@ /// +/// /// namespace ts { - export interface SourceFile { - fileWatcher?: FileWatcher; - } - interface Statistic { name: string; value: string; } - const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = { - getCurrentDirectory: () => sys.getCurrentDirectory(), - getNewLine: () => sys.newLine, - getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames) - }; - - let reportDiagnosticWorker = reportDiagnosticSimply; - - function reportDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost) { - reportDiagnosticWorker(diagnostic, host || defaultFormatDiagnosticsHost); - } - - function reportDiagnostics(diagnostics: Diagnostic[], host: FormatDiagnosticsHost): void { - for (const diagnostic of diagnostics) { - reportDiagnostic(diagnostic, host); - } - } - - function reportEmittedFiles(files: string[]): void { - if (!files || files.length === 0) { - return; - } - - const currentDir = sys.getCurrentDirectory(); - - for (const file of files) { - const filepath = getNormalizedAbsolutePath(file, currentDir); - - sys.write(`TSFILE: ${filepath}${sys.newLine}`); - } - } - function countLines(program: Program): number { let count = 0; forEach(program.getSourceFiles(), file => { @@ -56,25 +21,11 @@ namespace ts { return diagnostic.messageText; } - function reportDiagnosticSimply(diagnostic: Diagnostic, host: FormatDiagnosticsHost): void { - sys.write(ts.formatDiagnostics([diagnostic], host)); - } - - function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHost): void { - sys.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + sys.newLine); - } - - function reportWatchDiagnostic(diagnostic: Diagnostic) { - let output = new Date().toLocaleTimeString() + " - "; - - if (diagnostic.file) { - const loc = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); - output += `${ diagnostic.file.fileName }(${ loc.line + 1 },${ loc.character + 1 }): `; + let reportDiagnostic = createDiagnosticReporter(sys, reportDiagnosticSimply); + function udpateReportDiagnostic(options: CompilerOptions) { + if (options.pretty) { + reportDiagnostic = createDiagnosticReporter(sys, reportDiagnosticWithColorAndContext); } - - output += `${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine + sys.newLine + sys.newLine }`; - - sys.write(output); } function padLeft(s: string, length: number) { @@ -98,26 +49,12 @@ namespace ts { export function executeCommandLine(args: string[]): void { const commandLine = parseCommandLine(args); - let configFileName: string; // Configuration file name (if any) - let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any) - let configFileWatcher: FileWatcher; // Configuration file watcher - let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal - let cachedProgram: Program; // Program cached from last compilation - let rootFileNames: string[]; // Root fileNames for compilation - let compilerOptions: CompilerOptions; // Compiler options for compilation - let compilerHost: CompilerHost; // Compiler host - let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host - let timerHandleForRecompilation: any; // Handle for 0.25s wait timer to trigger recompilation - let timerHandleForDirectoryChanges: any; // Handle for 0.25s wait timer to trigger directory change handler - - // This map stores and reuses results of fileExists check that happen inside 'createProgram' - // This allows to save time in module resolution heavy scenarios when existence of the same file might be checked multiple times. - let cachedExistingFiles: Map; - let hostFileExists: typeof compilerHost.fileExists; + // Configuration file name (if any) + let configFileName: string; if (commandLine.options.locale) { if (!isJSONSupported()) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale"), /* host */ undefined); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale")); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } validateLocaleAndSetLanguage(commandLine.options.locale, sys, commandLine.errors); @@ -126,7 +63,7 @@ namespace ts { // If there are any errors due to command line parsing and/or // setting up localization, report them and quit. if (commandLine.errors.length > 0) { - reportDiagnostics(commandLine.errors, compilerHost); + reportDiagnostics(commandLine.errors, reportDiagnostic); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } @@ -148,11 +85,11 @@ namespace ts { if (commandLine.options.project) { if (!isJSONSupported()) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--project"), /* host */ undefined); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--project")); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } if (commandLine.fileNames.length !== 0) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line), /* host */ undefined); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line)); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } @@ -160,14 +97,14 @@ namespace ts { if (!fileOrDirectory /* current directory "." */ || sys.directoryExists(fileOrDirectory)) { configFileName = combinePaths(fileOrDirectory, "tsconfig.json"); if (!sys.fileExists(configFileName)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project), /* host */ undefined); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project)); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } } else { configFileName = fileOrDirectory; if (!sys.fileExists(configFileName)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project), /* host */ undefined); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project)); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } } @@ -183,249 +120,94 @@ namespace ts { return sys.exit(ExitStatus.Success); } - if (isWatchSet(commandLine.options)) { - if (!sys.watchFile) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"), /* host */ undefined); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - if (configFileName) { - configFileWatcher = sys.watchFile(configFileName, configFileChanged); - } - if (sys.watchDirectory && configFileName) { - const directory = ts.getDirectoryPath(configFileName); - directoryWatcher = sys.watchDirectory( - // When the configFileName is just "tsconfig.json", the watched directory should be - // the current directory; if there is a given "project" parameter, then the configFileName - // is an absolute file name. - directory === "" ? "." : directory, - watchedDirectoryChanged, /*recursive*/ true); - } - } - - performCompilation(); - - function parseConfigFile(): ParsedCommandLine { - if (!cachedConfigFileText) { - try { - cachedConfigFileText = sys.readFile(configFileName); - } - catch (e) { - const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); - reportWatchDiagnostic(error); - sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - return; - } - } - if (!cachedConfigFileText) { - const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); - reportDiagnostics([error], /* compilerHost */ undefined); - sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - return; - } - - const result = parseJsonText(configFileName, cachedConfigFileText); - reportDiagnostics(result.parseDiagnostics, /* compilerHost */ undefined); - - const cwd = sys.getCurrentDirectory(); - const configParseResult = parseJsonSourceFileConfigFileContent(result, sys, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), commandLine.options, getNormalizedAbsolutePath(configFileName, cwd)); - reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined); - + const commandLineOptions = commandLine.options; + if (configFileName) { + const reportWatchDiagnostic = createWatchDiagnosticReporter(); + const configParseResult = parseConfigFile(configFileName, commandLineOptions, sys, reportDiagnostic, reportWatchDiagnostic); + udpateReportDiagnostic(configParseResult.options); if (isWatchSet(configParseResult.options)) { - if (!sys.watchFile) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"), /* host */ undefined); - sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - if (!directoryWatcher && sys.watchDirectory && configFileName) { - const directory = ts.getDirectoryPath(configFileName); - directoryWatcher = sys.watchDirectory( - // When the configFileName is just "tsconfig.json", the watched directory should be - // the current directory; if there is a given "project" parameter, then the configFileName - // is an absolute file name. - directory === "" ? "." : directory, - watchedDirectoryChanged, /*recursive*/ true); - } + reportWatchModeWithoutSysSupport(); + createWatchModeWithConfigFile(configParseResult, commandLineOptions, createWatchingSystemHost(reportWatchDiagnostic)); } - return configParseResult; - } - - // Invoked to perform initial compilation or re-compilation in watch mode - function performCompilation() { - - if (!cachedProgram) { - if (configFileName) { - const configParseResult = parseConfigFile(); - rootFileNames = configParseResult.fileNames; - compilerOptions = configParseResult.options; - } - else { - rootFileNames = commandLine.fileNames; - compilerOptions = commandLine.options; - } - compilerHost = createCompilerHost(compilerOptions); - hostGetSourceFile = compilerHost.getSourceFile; - compilerHost.getSourceFile = getSourceFile; - - hostFileExists = compilerHost.fileExists; - compilerHost.fileExists = cachedFileExists; - } - - if (compilerOptions.pretty) { - reportDiagnosticWorker = reportDiagnosticWithColorAndContext; - } - - // reset the cache of existing files - cachedExistingFiles = createMap(); - - const compileResult = compile(rootFileNames, compilerOptions, compilerHost); - - if (!isWatchSet(compilerOptions)) { - return sys.exit(compileResult.exitStatus); - } - - setCachedProgram(compileResult.program); - reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); - - const missingPaths = compileResult.program.getMissingFilePaths(); - missingPaths.forEach(path => { - const fileWatcher = sys.watchFile(path, (_fileName, eventKind) => { - if (eventKind === FileWatcherEventKind.Created) { - fileWatcher.close(); - startTimerForRecompilation(); - } - }); - }); - } - - function cachedFileExists(fileName: string): boolean { - let fileExists = cachedExistingFiles.get(fileName); - if (fileExists === undefined) { - cachedExistingFiles.set(fileName, fileExists = hostFileExists(fileName)); - } - return fileExists; - } - - function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) { - // Return existing SourceFile object if one is available - if (cachedProgram) { - const sourceFile = cachedProgram.getSourceFile(fileName); - // A modified source file has no watcher and should not be reused - if (sourceFile && sourceFile.fileWatcher) { - return sourceFile; - } - } - // Use default host function - const sourceFile = hostGetSourceFile(fileName, languageVersion, onError); - if (sourceFile && isWatchSet(compilerOptions) && sys.watchFile) { - // Attach a file watcher - sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (_fileName, eventKind) => sourceFileChanged(sourceFile, eventKind)); - } - return sourceFile; - } - - // Change cached program to the given program - function setCachedProgram(program: Program) { - if (cachedProgram) { - const newSourceFiles = program ? program.getSourceFiles() : undefined; - forEach(cachedProgram.getSourceFiles(), sourceFile => { - if (!(newSourceFiles && contains(newSourceFiles, sourceFile))) { - if (sourceFile.fileWatcher) { - sourceFile.fileWatcher.close(); - sourceFile.fileWatcher = undefined; - } - } - }); - } - cachedProgram = program; - } - - // If a source file changes, mark it as unwatched and start the recompilation timer - function sourceFileChanged(sourceFile: SourceFile, eventKind: FileWatcherEventKind) { - sourceFile.fileWatcher.close(); - sourceFile.fileWatcher = undefined; - if (eventKind === FileWatcherEventKind.Deleted) { - unorderedRemoveItem(rootFileNames, sourceFile.fileName); - } - startTimerForRecompilation(); - } - - // If the configuration file changes, forget cached program and start the recompilation timer - function configFileChanged() { - setCachedProgram(undefined); - cachedConfigFileText = undefined; - startTimerForRecompilation(); - } - - function watchedDirectoryChanged(fileName: string) { - if (fileName && !ts.isSupportedSourceFileName(fileName, compilerOptions)) { - return; - } - - startTimerForHandlingDirectoryChanges(); - } - - function startTimerForHandlingDirectoryChanges() { - if (!sys.setTimeout || !sys.clearTimeout) { - return; - } - - if (timerHandleForDirectoryChanges) { - sys.clearTimeout(timerHandleForDirectoryChanges); - } - timerHandleForDirectoryChanges = sys.setTimeout(directoryChangeHandler, 250); - } - - function directoryChangeHandler() { - const parsedCommandLine = parseConfigFile(); - const newFileNames = ts.map(parsedCommandLine.fileNames, compilerHost.getCanonicalFileName); - const canonicalRootFileNames = ts.map(rootFileNames, compilerHost.getCanonicalFileName); - - // We check if the project file list has changed. If so, we just throw away the old program and start fresh. - if (!arrayIsEqualTo(newFileNames && newFileNames.sort(), canonicalRootFileNames && canonicalRootFileNames.sort())) { - setCachedProgram(undefined); - startTimerForRecompilation(); + else { + performCompilation(configParseResult.fileNames, configParseResult.options); } } - - // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch - // operations (such as saving all modified files in an editor) a chance to complete before we kick - // off a new compilation. - function startTimerForRecompilation() { - if (!sys.setTimeout || !sys.clearTimeout) { - return; + else { + udpateReportDiagnostic(commandLineOptions); + if (isWatchSet(commandLineOptions)) { + reportWatchModeWithoutSysSupport(); + createWatchModeWithoutConfigFile(commandLine.fileNames, commandLineOptions, createWatchingSystemHost()); } - - if (timerHandleForRecompilation) { - sys.clearTimeout(timerHandleForRecompilation); + else { + performCompilation(commandLine.fileNames, commandLineOptions); } - timerHandleForRecompilation = sys.setTimeout(recompile, 250); - } - - function recompile() { - timerHandleForRecompilation = undefined; - reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation)); - performCompilation(); } } - function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) { - const hasDiagnostics = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics; - let statistics: Statistic[]; - if (hasDiagnostics) { + function reportWatchModeWithoutSysSupport() { + if (!sys.watchFile || !sys.watchDirectory) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch")); + sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + } + + function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions) { + const compilerHost = createCompilerHost(compilerOptions); + enableStatistics(compilerOptions); + + const program = createProgram(rootFileNames, compilerOptions, compilerHost); + const exitStatus = compileProgram(program); + + reportStatistics(program); + return sys.exit(exitStatus); + } + + function createWatchingSystemHost(reportWatchDiagnostic?: DiagnosticReporter) { + const watchingHost = ts.createWatchingSystemHost(/*pretty*/ undefined, sys, parseConfigFile, reportDiagnostic, reportWatchDiagnostic); + watchingHost.beforeCompile = enableStatistics; + const afterCompile = watchingHost.afterCompile; + watchingHost.afterCompile = (host, program, builder) => { + afterCompile(host, program, builder); + reportStatistics(program); + }; + return watchingHost; + } + + function compileProgram(program: Program): ExitStatus { + let diagnostics: Diagnostic[]; + + // First get and report any syntactic errors. + diagnostics = program.getSyntacticDiagnostics(); + + // If we didn't have any syntactic errors, then also try getting the global and + // semantic errors. + if (diagnostics.length === 0) { + diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()); + + if (diagnostics.length === 0) { + diagnostics = program.getSemanticDiagnostics(); + } + } + + // Emit and report any errors we ran into. + const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(); + diagnostics = diagnostics.concat(emitDiagnostics); + + return handleEmitOutputAndReportErrors(sys, program, emittedFiles, emitSkipped, diagnostics, reportDiagnostic); + } + + function enableStatistics(compilerOptions: CompilerOptions) { + if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { performance.enable(); + } + } + + function reportStatistics(program: Program) { + let statistics: Statistic[]; + const compilerOptions = program.getCompilerOptions(); + if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { statistics = []; - } - - const program = createProgram(fileNames, compilerOptions, compilerHost); - const exitStatus = compileProgram(); - - if (compilerOptions.listFiles) { - forEach(program.getSourceFiles(), file => { - sys.write(file.fileName + sys.newLine); - }); - } - - if (hasDiagnostics) { const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; reportCountStatistic("Files", program.getSourceFiles().length); reportCountStatistic("Lines", countLines(program)); @@ -463,44 +245,6 @@ namespace ts { performance.disable(); } - return { program, exitStatus }; - - function compileProgram(): ExitStatus { - let diagnostics: Diagnostic[]; - - // First get and report any syntactic errors. - diagnostics = program.getSyntacticDiagnostics(); - - // If we didn't have any syntactic errors, then also try getting the global and - // semantic errors. - if (diagnostics.length === 0) { - diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()); - - if (diagnostics.length === 0) { - diagnostics = program.getSemanticDiagnostics(); - } - } - - // Otherwise, emit and report any errors we ran into. - const emitOutput = program.emit(); - diagnostics = diagnostics.concat(emitOutput.diagnostics); - - reportDiagnostics(sortAndDeduplicateDiagnostics(diagnostics), compilerHost); - - reportEmittedFiles(emitOutput.emittedFiles); - - if (emitOutput.emitSkipped && diagnostics.length > 0) { - // If the emitter didn't emit anything, then pass that value along. - return ExitStatus.DiagnosticsPresent_OutputsSkipped; - } - else if (diagnostics.length > 0) { - // The emitter emitted something, inform the caller if that happened in the presence - // of diagnostics or not. - return ExitStatus.DiagnosticsPresent_OutputsGenerated; - } - return ExitStatus.Success; - } - function reportStatistics() { let nameSize = 0; let valueSize = 0; @@ -654,19 +398,17 @@ namespace ts { const currentDirectory = sys.getCurrentDirectory(); const file = normalizePath(combinePaths(currentDirectory, "tsconfig.json")); if (sys.fileExists(file)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file), /* host */ undefined); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file)); } else { sys.writeFile(file, generateTSConfig(options, fileNames, sys.newLine)); - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file), /* host */ undefined); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file)); } return; } } -ts.setStackTraceLimit(); - if (ts.Debug.isDebugging) { ts.Debug.enableDebugInfo(); } @@ -674,5 +416,4 @@ if (ts.Debug.isDebugging) { if (ts.sys.tryEnableSourceMapsForHost && /^development$/i.test(ts.sys.getEnvironmentVariable("NODE_ENV"))) { ts.sys.tryEnableSourceMapsForHost(); } - ts.executeCommandLine(ts.sys.args); diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 3709d65b7f..1001f51f33 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -36,6 +36,9 @@ "declarationEmitter.ts", "emitter.ts", "program.ts", + "builder.ts", + "resolutionCache.ts", + "watchedProgram.ts", "commandLineParser.ts", "tsc.ts", "diagnosticInformationMap.generated.ts" diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 42d551c65f..026a8bf863 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2347,6 +2347,7 @@ namespace ts { /* @internal */ patternAmbientModules?: PatternAmbientModule[]; /* @internal */ ambientModuleNames: ReadonlyArray; /* @internal */ checkJsDirective: CheckJsDirective | undefined; + /* @internal */ version: string; } export interface Bundle extends Node { @@ -2410,7 +2411,7 @@ namespace ts { * program source file but could not be located. */ /* @internal */ - getMissingFilePaths(): Path[]; + getMissingFilePaths(): ReadonlyArray; /** * Emits the JavaScript and declaration files. If targetSourceFile is not specified, then @@ -3570,6 +3571,7 @@ namespace ts { charset?: string; checkJs?: boolean; /* @internal */ configFilePath?: string; + /** configFile is set as non enumerable property so as to avoid checking of json source files */ /* @internal */ readonly configFile?: JsonSourceFile; declaration?: boolean; declarationDir?: string; @@ -4014,7 +4016,7 @@ namespace ts { export interface ResolvedModuleWithFailedLookupLocations { readonly resolvedModule: ResolvedModuleFull | undefined; /* @internal */ - readonly failedLookupLocations: string[]; + readonly failedLookupLocations: ReadonlyArray; /*@internal*/ isInvalidated?: boolean; } @@ -4028,14 +4030,18 @@ namespace ts { export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective; - readonly failedLookupLocations: string[]; + readonly failedLookupLocations: ReadonlyArray; /*@internal*/ isInvalidated?: boolean; } + export interface HasInvalidatedResolution { + (sourceFile: Path): boolean; + } + export interface CompilerHost extends ModuleResolutionHost { - getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; - getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; + getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile; + getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile; getCancellationToken?(): CancellationToken; getDefaultLibFileName(options: CompilerOptions): string; getDefaultLibLocation?(): string; @@ -4059,6 +4065,8 @@ namespace ts { */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; getEnvironmentVariable?(name: string): string; + onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void; + hasInvalidatedResolution?: HasInvalidatedResolution; } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f1a8dfd676..f09ce44649 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -366,7 +366,7 @@ namespace ts { } export function getTextOfConstantValue(value: string | number) { - return typeof value === "string" ? '"' + escapeNonAsciiString(value) + '"' : "" + value; + return isString(value) ? '"' + escapeNonAsciiString(value) + '"' : "" + value; } // Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__' @@ -403,7 +403,10 @@ namespace ts { ((node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(node)); } - /* @internal */ + export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration { + return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral; + } + export function isNonGlobalAmbientModule(node: Node): node is ModuleDeclaration & { name: StringLiteral } { return isModuleDeclaration(node) && isStringLiteral(node.name); } @@ -1416,8 +1419,8 @@ namespace ts { if (node.kind === SyntaxKind.ExportDeclaration) { return (node).moduleSpecifier; } - if (node.kind === SyntaxKind.ModuleDeclaration && (node).name.kind === SyntaxKind.StringLiteral) { - return (node).name; + if (isModuleWithStringLiteralName(node)) { + return node.name; } } @@ -3202,17 +3205,14 @@ namespace ts { const carriageReturnLineFeed = "\r\n"; const lineFeed = "\n"; - export function getNewLineCharacter(options: CompilerOptions | PrinterOptions): string { + export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, system?: System): string { switch (options.newLine) { case NewLineKind.CarriageReturnLineFeed: return carriageReturnLineFeed; case NewLineKind.LineFeed: return lineFeed; } - if (sys) { - return sys.newLine; - } - return carriageReturnLineFeed; + return system ? system.newLine : sys ? sys.newLine : carriageReturnLineFeed; } /** @@ -3466,6 +3466,84 @@ namespace ts { export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags { return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags; } + + export function compareDataObjects(dst: any, src: any): boolean { + if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) { + return false; + } + + for (const e in dst) { + if (typeof dst[e] === "object") { + if (!compareDataObjects(dst[e], src[e])) { + return false; + } + } + else if (typeof dst[e] !== "function") { + if (dst[e] !== src[e]) { + return false; + } + } + } + return true; + } + + /** + * clears already present map by calling onDeleteExistingValue callback before deleting that key/value + */ + export function clearMap(map: Map, onDeleteValue: (key: string, existingValue: T) => void) { + // Remove all + map.forEach((existingValue, key) => { + onDeleteValue(key, existingValue); + }); + map.clear(); + } + + export interface MutateMapOptions { + createNewValue(key: string, valueInNewMap: U): T; + onDeleteValue(key: string, existingValue: T): void; + + /** + * If present this is called with the key when there is value for that key both in new map as well as existing map provided + * Caller can then decide to update or remove this key. + * If the key is removed, caller will get callback of createNewValue for that key. + * If this callback is not provided, the value of such keys is not updated. + */ + onExistingValue?(key: string, existingValue: T, valueInNewMap: U): void; + } + + /** + * Mutates the map with newMap such that keys in map will be same as newMap. + */ + export function mutateMap(map: Map, newMap: ReadonlyMap, options: MutateMapOptions) { + // If there are new values update them + if (newMap) { + const { createNewValue, onDeleteValue, onExistingValue } = options; + // Needs update + map.forEach((existingValue, key) => { + const valueInNewMap = newMap.get(key); + // Not present any more in new map, remove it + if (valueInNewMap === undefined) { + map.delete(key); + onDeleteValue(key, existingValue); + } + // If present notify about existing values + else if (onExistingValue) { + onExistingValue(key, existingValue, valueInNewMap); + } + }); + + // Add new values that are not already present + newMap.forEach((valueInNewMap, key) => { + if (!map.has(key)) { + // New values + map.set(key, createNewValue(key, valueInNewMap)); + } + }); + } + else { + clearMap(map, options.onDeleteValue); + } + } } namespace ts { diff --git a/src/compiler/watchedProgram.ts b/src/compiler/watchedProgram.ts new file mode 100644 index 0000000000..084c707cea --- /dev/null +++ b/src/compiler/watchedProgram.ts @@ -0,0 +1,712 @@ +/// +/// +/// + +namespace ts { + export type DiagnosticReporter = (diagnostic: Diagnostic) => void; + export type DiagnosticWorker = (diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System) => void; + export type ParseConfigFile = (configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter) => ParsedCommandLine; + export interface WatchingSystemHost { + // FS system to use + system: System; + + // parse config file + parseConfigFile: ParseConfigFile; + + // Reporting errors + reportDiagnostic: DiagnosticReporter; + reportWatchDiagnostic: DiagnosticReporter; + + // Callbacks to do custom action before creating program and after creating program + beforeCompile(compilerOptions: CompilerOptions): void; + afterCompile(host: System, program: Program, builder: Builder): void; + } + + const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? { + getCurrentDirectory: () => sys.getCurrentDirectory(), + getNewLine: () => sys.newLine, + getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames) + } : undefined; + + export function createDiagnosticReporter(system = sys, worker = reportDiagnosticSimply, formatDiagnosticsHost?: FormatDiagnosticsHost): DiagnosticReporter { + return diagnostic => worker(diagnostic, getFormatDiagnosticsHost(), system); + + function getFormatDiagnosticsHost() { + return formatDiagnosticsHost || (formatDiagnosticsHost = system === sys ? defaultFormatDiagnosticsHost : { + getCurrentDirectory: () => system.getCurrentDirectory(), + getNewLine: () => system.newLine, + getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), + }); + } + } + + export function createWatchDiagnosticReporter(system = sys): DiagnosticReporter { + return diagnostic => { + let output = new Date().toLocaleTimeString() + " - "; + output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${system.newLine + system.newLine + system.newLine}`; + system.write(output); + }; + } + + export function reportDiagnostics(diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter): void { + for (const diagnostic of diagnostics) { + reportDiagnostic(diagnostic); + } + } + + export function reportDiagnosticSimply(diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System): void { + system.write(ts.formatDiagnostic(diagnostic, host)); + } + + export function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System): void { + system.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + host.getNewLine()); + } + + export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter): ParsedCommandLine { + let configFileText: string; + try { + configFileText = system.readFile(configFileName); + } + catch (e) { + const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); + reportWatchDiagnostic(error); + system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return; + } + if (!configFileText) { + const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); + reportDiagnostics([error], reportDiagnostic); + system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return; + } + + const result = parseJsonText(configFileName, configFileText); + reportDiagnostics(result.parseDiagnostics, reportDiagnostic); + + const cwd = system.getCurrentDirectory(); + const configParseResult = parseJsonSourceFileConfigFileContent(result, system, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); + reportDiagnostics(configParseResult.errors, reportDiagnostic); + + return configParseResult; + } + + function reportEmittedFiles(files: string[], system: System): void { + if (!files || files.length === 0) { + return; + } + const currentDir = system.getCurrentDirectory(); + for (const file of files) { + const filepath = getNormalizedAbsolutePath(file, currentDir); + system.write(`TSFILE: ${filepath}${system.newLine}`); + } + } + + export function handleEmitOutputAndReportErrors(system: System, program: Program, + emittedFiles: string[], emitSkipped: boolean, + diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter + ): ExitStatus { + reportDiagnostics(sortAndDeduplicateDiagnostics(diagnostics), reportDiagnostic); + reportEmittedFiles(emittedFiles, system); + + if (program.getCompilerOptions().listFiles) { + forEach(program.getSourceFiles(), file => { + system.write(file.fileName + system.newLine); + }); + } + + if (emitSkipped && diagnostics.length > 0) { + // If the emitter didn't emit anything, then pass that value along. + return ExitStatus.DiagnosticsPresent_OutputsSkipped; + } + else if (diagnostics.length > 0) { + // The emitter emitted something, inform the caller if that happened in the presence + // of diagnostics or not. + return ExitStatus.DiagnosticsPresent_OutputsGenerated; + } + return ExitStatus.Success; + } + + export function createWatchingSystemHost(pretty?: DiagnosticStyle, system = sys, + parseConfigFile?: ParseConfigFile, reportDiagnostic?: DiagnosticReporter, + reportWatchDiagnostic?: DiagnosticReporter + ): WatchingSystemHost { + reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system, pretty ? reportDiagnosticWithColorAndContext : reportDiagnosticSimply); + reportWatchDiagnostic = reportWatchDiagnostic || createWatchDiagnosticReporter(system); + parseConfigFile = parseConfigFile || ts.parseConfigFile; + return { + system, + parseConfigFile, + reportDiagnostic, + reportWatchDiagnostic, + beforeCompile: noop, + afterCompile: compileWatchedProgram, + }; + + function compileWatchedProgram(host: System, program: Program, builder: Builder) { + // First get and report any syntactic errors. + let diagnostics = program.getSyntacticDiagnostics(); + let reportSemanticDiagnostics = false; + + // If we didn't have any syntactic errors, then also try getting the global and + // semantic errors. + if (diagnostics.length === 0) { + diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()); + + if (diagnostics.length === 0) { + reportSemanticDiagnostics = true; + } + } + + // Emit and report any errors we ran into. + const emittedFiles: string[] = program.getCompilerOptions().listEmittedFiles ? [] : undefined; + let sourceMaps: SourceMapData[]; + let emitSkipped: boolean; + + const result = builder.emitChangedFiles(program); + if (result.length === 0) { + emitSkipped = true; + } + else { + for (const emitOutput of result) { + if (emitOutput.emitSkipped) { + emitSkipped = true; + } + diagnostics = concatenate(diagnostics, emitOutput.diagnostics); + sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps); + writeOutputFiles(emitOutput.outputFiles); + } + } + + if (reportSemanticDiagnostics) { + diagnostics = diagnostics.concat(builder.getSemanticDiagnostics(program)); + } + return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped, + diagnostics, reportDiagnostic); + + function ensureDirectoriesExist(directoryPath: string) { + if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) { + const parentDirectory = getDirectoryPath(directoryPath); + ensureDirectoriesExist(parentDirectory); + host.createDirectory(directoryPath); + } + } + + function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) { + try { + performance.mark("beforeIOWrite"); + ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); + + host.writeFile(fileName, data, writeByteOrderMark); + + performance.mark("afterIOWrite"); + performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); + } + catch (e) { + return createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, e); + } + } + + function writeOutputFiles(outputFiles: OutputFile[]) { + if (outputFiles) { + for (const outputFile of outputFiles) { + const error = writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark); + if (error) { + diagnostics.push(error); + } + if (emittedFiles) { + emittedFiles.push(outputFile.name); + } + } + } + } + } + } + + export function createWatchModeWithConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions = {}, watchingHost?: WatchingSystemHost) { + return createWatchMode(configParseResult.fileNames, configParseResult.options, watchingHost, configParseResult.options.configFilePath, configParseResult.configFileSpecs, configParseResult.wildcardDirectories, optionsToExtend); + } + + export function createWatchModeWithoutConfigFile(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost) { + return createWatchMode(rootFileNames, compilerOptions, watchingHost); + } + + interface HostFileInfo { + version: number; + sourceFile: SourceFile; + fileWatcher: FileWatcher; + } + + function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike, optionsToExtendForConfigFile?: CompilerOptions) { + let program: Program; + let needsReload: boolean; // true if the config file changed and needs to reload it from the disk + let missingFilesMap: Map; // Map of file watchers for the missing files + let configFileWatcher: FileWatcher; // watcher for the config file + let watchedWildcardDirectories: Map; // map of watchers for the wild card directories in the config file + let timerToUpdateProgram: any; // timer callback to recompile the program + + const sourceFilesCache = createMap(); // Cache that stores the source file and version info + let missingFilePathsRequestedForRelease: Path[]; // These paths are held temparirly so that we can remove the entry from source file cache if the file is not tracked by missing files + let hasInvalidatedResolution: HasInvalidatedResolution; // Passed along to see if source file has invalidated resolutions + let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations + + watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty); + const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost; + + let host: System; + if (configFileName) { + host = createCachedSystem(system); + configFileWatcher = system.watchFile(configFileName, onConfigFileChanged); + } + else { + host = system; + } + const currentDirectory = host.getCurrentDirectory(); + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); + + // Cache for the module resolution + const resolutionCache = createResolutionCache( + fileName => toPath(fileName), + () => compilerOptions, + watchFailedLookupLocation, + s => writeLog(s) + ); + + // There is no extra check needed since we can just rely on the program to decide emit + const builder = createBuilder(getCanonicalFileName, getFileEmitOutput, computeHash, _sourceFile => true); + + synchronizeProgram(); + + // Update the wild card directory watch + watchConfigFileWildCardDirectories(); + + return () => program; + + function synchronizeProgram() { + writeLog(`Synchronizing program`); + + hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(); + if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution)) { + return; + } + + // Create the compiler host + const compilerHost = createWatchedCompilerHost(compilerOptions); + resolutionCache.setModuleResolutionHost(compilerHost); + if (hasChangedCompilerOptions && changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) { + resolutionCache.clear(); + } + hasChangedCompilerOptions = false; + beforeCompile(compilerOptions); + + // Compile the program + program = createProgram(rootFileNames, compilerOptions, compilerHost, program); + builder.onProgramUpdateGraph(program, hasInvalidatedResolution); + + // Update watches + updateMissingFilePathsWatch(program, missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath, closeMissingFilePathWatcher); + if (missingFilePathsRequestedForRelease) { + // These are the paths that program creater told us as not in use any more but were missing on the disk. + // We didnt remove the entry for them from sourceFiles cache so that we dont have to do File IO, + // if there is already watcher for it (for missing files) + // At that point our watches were updated, hence now we know that these paths are not tracked and need to be removed + // so that at later time we have correct result of their presence + for (const missingFilePath of missingFilePathsRequestedForRelease) { + if (!missingFilesMap.has(missingFilePath)) { + sourceFilesCache.delete(missingFilePath); + } + } + missingFilePathsRequestedForRelease = undefined; + } + + afterCompile(host, program, builder); + reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); + } + + function createWatchedCompilerHost(options: CompilerOptions): CompilerHost { + const newLine = getNewLineCharacter(options, system); + const realpath = host.realpath && ((path: string) => host.realpath(path)); + + return { + getSourceFile: getVersionedSourceFile, + getSourceFileByPath: getVersionedSourceFileByPath, + getDefaultLibLocation, + getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), + writeFile: (_fileName, _data, _writeByteOrderMark, _onError?, _sourceFiles?) => { }, + getCurrentDirectory: memoize(() => host.getCurrentDirectory()), + useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames, + getCanonicalFileName, + getNewLine: () => newLine, + fileExists, + readFile: fileName => host.readFile(fileName), + trace: (s: string) => host.write(s + newLine), + directoryExists: directoryName => host.directoryExists(directoryName), + getEnvironmentVariable: name => host.getEnvironmentVariable ? host.getEnvironmentVariable(name) : "", + getDirectories: (path: string) => host.getDirectories(path), + realpath, + resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile), + resolveModuleNames: (moduleNames, containingFile) => resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ false), + onReleaseOldSourceFile, + hasInvalidatedResolution + }; + } + + function toPath(fileName: string) { + return ts.toPath(fileName, currentDirectory, getCanonicalFileName); + } + + function fileExists(fileName: string) { + const path = toPath(fileName); + const hostSourceFileInfo = sourceFilesCache.get(path); + if (hostSourceFileInfo !== undefined) { + return !isString(hostSourceFileInfo); + } + + return host.fileExists(fileName); + } + + function getDefaultLibLocation(): string { + return getDirectoryPath(normalizePath(host.getExecutingFilePath())); + } + + function getVersionedSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile { + return getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile); + } + + function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile { + const hostSourceFile = sourceFilesCache.get(path); + // No source file on the host + if (isString(hostSourceFile)) { + return undefined; + } + + // Create new source file if requested or the versions dont match + if (!hostSourceFile || shouldCreateNewSourceFile || hostSourceFile.version.toString() !== hostSourceFile.sourceFile.version) { + const sourceFile = getNewSourceFile(); + if (hostSourceFile) { + if (shouldCreateNewSourceFile) { + hostSourceFile.version++; + } + if (sourceFile) { + hostSourceFile.sourceFile = sourceFile; + sourceFile.version = hostSourceFile.version.toString(); + if (!hostSourceFile.fileWatcher) { + hostSourceFile.fileWatcher = watchSourceFileForChanges(path); + } + } + else { + // There is no source file on host any more, close the watch, missing file paths will track it + hostSourceFile.fileWatcher.close(); + sourceFilesCache.set(path, hostSourceFile.version.toString()); + } + } + else { + let fileWatcher: FileWatcher; + if (sourceFile) { + sourceFile.version = "0"; + fileWatcher = watchSourceFileForChanges(path); + sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher }); + } + else { + sourceFilesCache.set(path, "0"); + } + } + return sourceFile; + } + return hostSourceFile.sourceFile; + + function getNewSourceFile() { + let text: string; + try { + performance.mark("beforeIORead"); + text = host.readFile(fileName, compilerOptions.charset); + performance.mark("afterIORead"); + performance.measure("I/O Read", "beforeIORead", "afterIORead"); + } + catch (e) { + if (onError) { + onError(e.message); + } + } + + return text !== undefined ? createSourceFile(fileName, text, languageVersion) : undefined; + } + } + + function removeSourceFile(path: Path) { + const hostSourceFile = sourceFilesCache.get(path); + if (hostSourceFile !== undefined) { + if (!isString(hostSourceFile)) { + hostSourceFile.fileWatcher.close(); + resolutionCache.invalidateResolutionOfFile(path); + } + sourceFilesCache.delete(path); + } + } + + function getSourceVersion(path: Path): string { + const hostSourceFile = sourceFilesCache.get(path); + return !hostSourceFile || isString(hostSourceFile) ? undefined : hostSourceFile.version.toString(); + } + + function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions) { + const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.path); + // If this is the source file thats in the cache and new program doesnt need it, + // remove the cached entry. + // Note we arent deleting entry if file became missing in new program or + // there was version update and new source file was created. + if (hostSourceFileInfo) { + // record the missing file paths so they can be removed later if watchers arent tracking them + if (isString(hostSourceFileInfo)) { + (missingFilePathsRequestedForRelease || (missingFilePathsRequestedForRelease = [])).push(oldSourceFile.path); + } + else if (hostSourceFileInfo.sourceFile === oldSourceFile) { + sourceFilesCache.delete(oldSourceFile.path); + resolutionCache.invalidateResolutionOfFile(oldSourceFile.path); + } + } + } + + // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch + // operations (such as saving all modified files in an editor) a chance to complete before we kick + // off a new compilation. + function scheduleProgramUpdate() { + if (!system.setTimeout || !system.clearTimeout) { + return; + } + + if (timerToUpdateProgram) { + system.clearTimeout(timerToUpdateProgram); + } + timerToUpdateProgram = system.setTimeout(updateProgram, 250); + } + + function scheduleProgramReload() { + Debug.assert(!!configFileName); + needsReload = true; + scheduleProgramUpdate(); + } + + function updateProgram() { + timerToUpdateProgram = undefined; + reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation)); + + if (needsReload) { + reloadConfigFile(); + } + else { + synchronizeProgram(); + } + } + + function reloadConfigFile() { + writeLog(`Reloading config file: ${configFileName}`); + needsReload = false; + + const cachedHost = host as CachedSystem; + cachedHost.clearCache(); + const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic); + rootFileNames = configParseResult.fileNames; + compilerOptions = configParseResult.options; + hasChangedCompilerOptions = true; + configFileSpecs = configParseResult.configFileSpecs; + configFileWildCardDirectories = configParseResult.wildcardDirectories; + + synchronizeProgram(); + + // Update the wild card directory watch + watchConfigFileWildCardDirectories(); + } + + function watchSourceFileForChanges(path: Path) { + return host.watchFile(path, (fileName, eventKind) => onSourceFileChange(fileName, path, eventKind)); + } + + function onSourceFileChange(fileName: string, path: Path, eventKind: FileWatcherEventKind) { + writeLog(`Source file path : ${path} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName}`); + + updateCachedSystemWithFile(fileName, path, eventKind); + const hostSourceFile = sourceFilesCache.get(path); + if (hostSourceFile) { + // Update the cache + if (eventKind === FileWatcherEventKind.Deleted) { + resolutionCache.invalidateResolutionOfFile(path); + if (!isString(hostSourceFile)) { + hostSourceFile.fileWatcher.close(); + sourceFilesCache.set(path, (hostSourceFile.version++).toString()); + } + } + else { + // Deleted file created + if (isString(hostSourceFile)) { + sourceFilesCache.delete(path); + } + else { + // file changed - just update the version + hostSourceFile.version++; + } + } + } + + // Update the program + scheduleProgramUpdate(); + } + + function updateCachedSystemWithFile(fileName: string, path: Path, eventKind: FileWatcherEventKind) { + if (configFileName) { + (host as CachedSystem).addOrDeleteFile(fileName, path, eventKind); + } + } + + function watchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) { + return host.watchFile(failedLookupLocation, (fileName, eventKind) => onFailedLookupLocationChange(fileName, eventKind, failedLookupLocation, failedLookupLocationPath, containingFile, name)); + } + + function onFailedLookupLocationChange(fileName: string, eventKind: FileWatcherEventKind, failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) { + writeLog(`Failed lookup location : ${failedLookupLocation} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName} containingFile: ${containingFile}, name: ${name}`); + updateCachedSystemWithFile(fileName, failedLookupLocationPath, eventKind); + resolutionCache.invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath); + scheduleProgramUpdate(); + } + + function watchMissingFilePath(missingFilePath: Path) { + return host.watchFile(missingFilePath, (fileName, eventKind) => onMissingFileChange(fileName, missingFilePath, eventKind)); + } + + function closeMissingFilePathWatcher(_missingFilePath: Path, fileWatcher: FileWatcher) { + fileWatcher.close(); + } + + function onMissingFileChange(fileName: string, missingFilePath: Path, eventKind: FileWatcherEventKind) { + writeLog(`Missing file path : ${missingFilePath} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName}`); + updateCachedSystemWithFile(fileName, missingFilePath, eventKind); + + if (eventKind === FileWatcherEventKind.Created && missingFilesMap.has(missingFilePath)) { + closeMissingFilePathWatcher(missingFilePath, missingFilesMap.get(missingFilePath)); + missingFilesMap.delete(missingFilePath); + + // Delete the entry in the source files cache so that new source file is created + removeSourceFile(missingFilePath); + + // When a missing file is created, we should update the graph. + scheduleProgramUpdate(); + } + } + + function watchConfigFileWildCardDirectories() { + updateWatchingWildcardDirectories( + watchedWildcardDirectories || (watchedWildcardDirectories = createMap()), + createMapFromTemplate(configFileWildCardDirectories), + watchWildCardDirectory, + stopWatchingWildCardDirectory + ); + } + + function watchWildCardDirectory(directory: string, flags: WatchDirectoryFlags) { + return host.watchDirectory(directory, fileOrFolder => + onFileAddOrRemoveInWatchedDirectory(getNormalizedAbsolutePath(fileOrFolder, directory)), + (flags & WatchDirectoryFlags.Recursive) !== 0); + } + + function stopWatchingWildCardDirectory(_directory: string, { watcher }: WildcardDirectoryWatcher, _recursiveChanged: boolean) { + watcher.close(); + } + + function onFileAddOrRemoveInWatchedDirectory(fileOrFolder: string) { + Debug.assert(!!configFileName); + + const fileOrFolderPath = toPath(fileOrFolder); + + // Since the file existance changed, update the sourceFiles cache + (host as CachedSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath); + removeSourceFile(fileOrFolderPath); + + // If a change was made inside "folder/file", node will trigger the callback twice: + // one with the fileName being "folder/file", and the other one with "folder". + // We don't respond to the second one. + if (fileOrFolder && !isSupportedSourceFileName(fileOrFolder, compilerOptions)) { + writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrFolder}`); + return; + } + + writeLog(`Project: ${configFileName} Detected file add/remove of supported extension: ${fileOrFolder}`); + + // Reload is pending, do the reload + if (!needsReload) { + const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, host); + if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) { + reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); + } + rootFileNames = result.fileNames; + + // Schedule Update the program + scheduleProgramUpdate(); + } + } + + function onConfigFileChanged(fileName: string, eventKind: FileWatcherEventKind) { + writeLog(`Config file : ${configFileName} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName}`); + scheduleProgramReload(); + } + + function writeLog(s: string) { + const hasDiagnostics = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics; + if (hasDiagnostics) { + host.write(s); + } + } + + function computeHash(data: string) { + return system.createHash ? system.createHash(data) : data; + } + } + + interface CachedSystem extends System, CachedHost { + } + + function createCachedSystem(host: System): CachedSystem { + const getFileSize = host.getFileSize ? (path: string) => host.getFileSize(path) : undefined; + const watchFile = host.watchFile ? (path: string, callback: FileWatcherCallback, pollingInterval?: number) => host.watchFile(path, callback, pollingInterval) : undefined; + const watchDirectory = host.watchDirectory ? (path: string, callback: DirectoryWatcherCallback, recursive?: boolean) => host.watchDirectory(path, callback, recursive) : undefined; + const getModifiedTime = host.getModifiedTime ? (path: string) => host.getModifiedTime(path) : undefined; + const createHash = host.createHash ? (data: string) => host.createHash(data) : undefined; + const getMemoryUsage = host.getMemoryUsage ? () => host.getMemoryUsage() : undefined; + const realpath = host.realpath ? (path: string) => host.realpath(path) : undefined; + const tryEnableSourceMapsForHost = host.tryEnableSourceMapsForHost ? () => host.tryEnableSourceMapsForHost() : undefined; + const setTimeout = host.setTimeout ? (callback: (...args: any[]) => void, ms: number, ...args: any[]) => host.setTimeout(callback, ms, ...args) : undefined; + const clearTimeout = host.clearTimeout ? (timeoutId: any) => host.clearTimeout(timeoutId) : undefined; + + const cachedPartialSystem = createCachedPartialSystem(host); + return { + args: host.args, + newLine: host.newLine, + useCaseSensitiveFileNames: host.useCaseSensitiveFileNames, + write: s => host.write(s), + readFile: (path, encoding?) => host.readFile(path, encoding), + getFileSize, + writeFile: (fileName, data, writeByteOrderMark?) => cachedPartialSystem.writeFile(fileName, data, writeByteOrderMark), + watchFile, + watchDirectory, + resolvePath: path => host.resolvePath(path), + fileExists: fileName => cachedPartialSystem.fileExists(fileName), + directoryExists: dir => cachedPartialSystem.directoryExists(dir), + createDirectory: dir => cachedPartialSystem.createDirectory(dir), + getExecutingFilePath: () => host.getExecutingFilePath(), + getCurrentDirectory: () => cachedPartialSystem.getCurrentDirectory(), + getDirectories: dir => cachedPartialSystem.getDirectories(dir), + readDirectory: (path, extensions, excludes, includes, depth) => cachedPartialSystem.readDirectory(path, extensions, excludes, includes, depth), + getModifiedTime, + createHash, + getMemoryUsage, + exit: exitCode => host.exit(exitCode), + realpath, + getEnvironmentVariable: name => host.getEnvironmentVariable(name), + tryEnableSourceMapsForHost, + debugMode: host.debugMode, + setTimeout, + clearTimeout, + addOrDeleteFileOrFolder: (fileOrFolder, fileOrFolderPath) => cachedPartialSystem.addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath), + addOrDeleteFile: (file, filePath, eventKind) => cachedPartialSystem.addOrDeleteFile(file, filePath, eventKind), + clearCache: () => cachedPartialSystem.clearCache() + }; + } +} diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 32c9fb67da..597224230b 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -394,7 +394,7 @@ namespace FourSlash { // Entry points from fourslash.ts public goToMarker(name: string | Marker = "") { - const marker = typeof name === "string" ? this.getMarkerByName(name) : name; + const marker = ts.isString(name) ? this.getMarkerByName(name) : name; if (this.activeFile.fileName !== marker.fileName) { this.openFile(marker.fileName); } @@ -403,7 +403,7 @@ namespace FourSlash { if (marker.position === -1 || marker.position > content.length) { throw new Error(`Marker "${name}" has been invalidated by unrecoverable edits to the file.`); } - const mName = typeof name === "string" ? name : this.markerName(marker); + const mName = ts.isString(name) ? name : this.markerName(marker); this.lastKnownMarker = mName; this.goToPosition(marker.position); } @@ -1026,7 +1026,7 @@ namespace FourSlash { public verifyNoReferences(markerNameOrRange?: string | Range) { if (markerNameOrRange) { - if (typeof markerNameOrRange === "string") { + if (ts.isString(markerNameOrRange)) { this.goToMarker(markerNameOrRange); } else { @@ -1522,7 +1522,7 @@ namespace FourSlash { resultString += "Diagnostics:" + Harness.IO.newLine(); const diagnostics = ts.getPreEmitDiagnostics(this.languageService.getProgram()); for (const diagnostic of diagnostics) { - if (typeof diagnostic.messageText !== "string") { + if (!ts.isString(diagnostic.messageText)) { let chainedMessage = diagnostic.messageText; let indentation = " "; while (chainedMessage) { @@ -2913,7 +2913,7 @@ namespace FourSlash { result = this.testData.files[index]; } } - else if (typeof indexOrName === "string") { + else if (ts.isString(indexOrName)) { let name = indexOrName; // names are stored in the compiler with this relative path, this allows people to use goTo.file on just the fileName diff --git a/src/harness/harness.ts b/src/harness/harness.ts index bf5cde2bc4..063f275f66 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -225,7 +225,7 @@ namespace Utils { return JSON.stringify(file, (_, v) => isNodeOrArray(v) ? serializeNode(v) : v, " "); function getKindName(k: number | string): string { - if (typeof k === "string") { + if (ts.isString(k)) { return k; } @@ -754,6 +754,10 @@ namespace Harness { } } + export function mockHash(s: string): string { + return `hash-${s}`; + } + const environment = Utils.getExecutionEnvironment(); switch (environment) { case Utils.ExecutionEnvironment.Node: diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 78da05570d..bf2977efd2 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -855,8 +855,4 @@ namespace Harness.LanguageService { getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); } getPreProcessedFileInfo(): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); } } - - export function mockHash(s: string): string { - return `hash-${s}`; - } } diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 752767d970..480feb547b 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -254,7 +254,7 @@ class ProjectRunner extends RunnerBase { if (option) { const optType = option.type; let value = testCase[name]; - if (typeof optType !== "string") { + if (!ts.isString(optType)) { const key = value.toLowerCase(); const optTypeValue = optType.get(key); if (optTypeValue) { diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 66ca2fc3f4..7c3ad0bc5a 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -1,4 +1,4 @@ -{ +{ "extends": "../tsconfig-base", "compilerOptions": { "removeComments": false, @@ -44,6 +44,7 @@ "../compiler/declarationEmitter.ts", "../compiler/emitter.ts", "../compiler/program.ts", + "../compiler/builder.ts", "../compiler/commandLineParser.ts", "../compiler/diagnosticInformationMap.generated.ts", "../services/breakpoints.ts", @@ -92,6 +93,7 @@ "rwcRunner.ts", "test262Runner.ts", "runner.ts", + "virtualFileSystemWithWatch.ts", "../server/protocol.ts", "../server/session.ts", "../server/client.ts", @@ -115,6 +117,7 @@ "./unittests/convertCompilerOptionsFromJson.ts", "./unittests/convertTypeAcquisitionFromJson.ts", "./unittests/tsserverProjectSystem.ts", + "./unittests/tscWatchMode.ts", "./unittests/matchFiles.ts", "./unittests/initializeTSConfig.ts", "./unittests/compileOnSave.ts", diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 74972d23d9..b7b382e5a1 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -47,7 +47,7 @@ namespace ts { clearTimeout, setImmediate: typeof setImmediate !== "undefined" ? setImmediate : action => setTimeout(action, 0), clearImmediate: typeof clearImmediate !== "undefined" ? clearImmediate : clearTimeout, - createHash: Harness.LanguageService.mockHash, + createHash: Harness.mockHash, }; } @@ -61,7 +61,7 @@ namespace ts { typingsInstaller: undefined }; const projectService = new server.ProjectService(svcOpts); - const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, /*containingProject*/ undefined); + const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, serverHost); const project = projectService.assignOrphanScriptInfoToInferredProject(rootScriptInfo); project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } ); @@ -188,7 +188,8 @@ namespace ts { let diags = project.getLanguageService().getSemanticDiagnostics(root.name); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); assert.isTrue(diags.length === 1, "one diagnostic expected"); - assert.isTrue(typeof diags[0].messageText === "string" && ((diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message"); + const messageText = diags[0].messageText; + assert.isTrue(isString(messageText) && messageText.indexOf("Cannot find module") === 0, "should be 'cannot find module' message"); fileMap.set(imported.name, imported); fileExistsCalledForBar = false; diff --git a/src/harness/unittests/configurationExtension.ts b/src/harness/unittests/configurationExtension.ts index 2d50d2cb2a..f3a5e93045 100644 --- a/src/harness/unittests/configurationExtension.ts +++ b/src/harness/unittests/configurationExtension.ts @@ -87,7 +87,7 @@ namespace ts { "/dev/tests/scenarios/first.json": "", "/dev/tests/baselines/first/output.ts": "" }); - const testContents = mapEntries(testContentsJson, (k, v) => [k, typeof v === "string" ? v : JSON.stringify(v)]); + const testContents = mapEntries(testContentsJson, (k, v) => [k, isString(v) ? v : JSON.stringify(v)]); const caseInsensitiveBasePath = "c:/dev/"; const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapEntries(testContents, (key, content) => [`c:${key}`, content])); diff --git a/src/harness/unittests/programMissingFiles.ts b/src/harness/unittests/programMissingFiles.ts index 668b684a25..ce8abc232e 100644 --- a/src/harness/unittests/programMissingFiles.ts +++ b/src/harness/unittests/programMissingFiles.ts @@ -1,6 +1,18 @@ /// namespace ts { + function verifyMissingFilePaths(missingPaths: ReadonlyArray, expected: ReadonlyArray) { + assert.isDefined(missingPaths); + const map = arrayToSet(expected) as Map; + for (const missing of missingPaths) { + const value = map.get(missing); + assert.isTrue(value, `${missing} to be ${value === undefined ? "not present" : "present only once"}, in actual: ${missingPaths} expected: ${expected}`); + map.set(missing, false); + } + const notFound = mapDefinedIter(map.keys(), k => map.get(k) === true ? k : undefined); + assert.equal(notFound.length, 0, `Not found ${notFound} in actual: ${missingPaths} expected: ${expected}`); + } + describe("Program.getMissingFilePaths", () => { const options: CompilerOptions = { @@ -40,34 +52,31 @@ namespace ts { it("handles no missing root files", () => { const program = createProgram([emptyFileRelativePath], options, testCompilerHost); const missing = program.getMissingFilePaths(); - assert.isDefined(missing); - assert.deepEqual(missing, []); + verifyMissingFilePaths(missing, []); }); it("handles missing root file", () => { const program = createProgram(["./nonexistent.ts"], options, testCompilerHost); const missing = program.getMissingFilePaths(); - assert.isDefined(missing); - assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path + verifyMissingFilePaths(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path }); it("handles multiple missing root files", () => { const program = createProgram(["./nonexistent0.ts", "./nonexistent1.ts"], options, testCompilerHost); - const missing = program.getMissingFilePaths().sort(); - assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]); + const missing = program.getMissingFilePaths(); + verifyMissingFilePaths(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]); }); it("handles a mix of present and missing root files", () => { const program = createProgram(["./nonexistent0.ts", emptyFileRelativePath, "./nonexistent1.ts"], options, testCompilerHost); - const missing = program.getMissingFilePaths().sort(); - assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]); + const missing = program.getMissingFilePaths(); + verifyMissingFilePaths(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]); }); it("handles repeatedly specified root files", () => { const program = createProgram(["./nonexistent.ts", "./nonexistent.ts"], options, testCompilerHost); const missing = program.getMissingFilePaths(); - assert.isDefined(missing); - assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]); + verifyMissingFilePaths(missing, ["d:/pretend/nonexistent.ts"]); }); it("normalizes file paths", () => { @@ -81,9 +90,8 @@ namespace ts { it("handles missing triple slash references", () => { const program = createProgram([referenceFileRelativePath], options, testCompilerHost); - const missing = program.getMissingFilePaths().sort(); - assert.isDefined(missing); - assert.deepEqual(missing, [ + const missing = program.getMissingFilePaths(); + verifyMissingFilePaths(missing, [ // From absolute reference "d:/imaginary/nonexistent1.ts", @@ -100,4 +108,4 @@ namespace ts { ]); }); }); -} \ No newline at end of file +} diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 5062f4c4b3..f2e269763a 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -346,7 +346,7 @@ namespace ts { const program_2 = updateProgram(program_1, ["a.ts"], options, noop, newTexts); assert.deepEqual(emptyArray, program_2.getMissingFilePaths()); - assert.equal(StructureIsReused.SafeModules, program_1.structureIsReused); + assert.equal(StructureIsReused.Not, program_1.structureIsReused); }); it("resolution cache follows imports", () => { diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 18109cfa9d..0a60935e28 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -25,7 +25,7 @@ namespace ts.server { clearTimeout: noop, setImmediate: () => 0, clearImmediate: noop, - createHash: Harness.LanguageService.mockHash, + createHash: Harness.mockHash, }; class TestSession extends Session { diff --git a/src/harness/unittests/telemetry.ts b/src/harness/unittests/telemetry.ts index 7f3428c839..e7a64c5990 100644 --- a/src/harness/unittests/telemetry.ts +++ b/src/harness/unittests/telemetry.ts @@ -53,7 +53,7 @@ namespace ts.projectSystem { // TODO: Apparently compilerOptions is mutated, so have to repeat it here! et.assertProjectInfoTelemetryEvent({ - projectId: Harness.LanguageService.mockHash("/hunter2/foo.csproj"), + projectId: Harness.mockHash("/hunter2/foo.csproj"), compilerOptions: { strict: true }, compileOnSave: true, // These properties can't be present for an external project, so they are undefined instead of false. @@ -195,7 +195,7 @@ namespace ts.projectSystem { const et = new EventTracker([jsconfig, file]); et.service.openClientFile(file.path); et.assertProjectInfoTelemetryEvent({ - projectId: Harness.LanguageService.mockHash("/jsconfig.json"), + projectId: Harness.mockHash("/jsconfig.json"), fileStats: fileStats({ js: 1 }), compilerOptions: autoJsCompilerOptions, typeAcquisition: { @@ -215,7 +215,7 @@ namespace ts.projectSystem { et.service.openClientFile(file.path); et.getEvent(server.ProjectLanguageServiceStateEvent, /*mayBeMore*/ true); et.assertProjectInfoTelemetryEvent({ - projectId: Harness.LanguageService.mockHash("/jsconfig.json"), + projectId: Harness.mockHash("/jsconfig.json"), fileStats: fileStats({ js: 1 }), compilerOptions: autoJsCompilerOptions, configFileName: "jsconfig.json", @@ -251,7 +251,7 @@ namespace ts.projectSystem { assertProjectInfoTelemetryEvent(partial: Partial): void { assert.deepEqual(this.getEvent(ts.server.ProjectInfoTelemetryEvent), { - projectId: Harness.LanguageService.mockHash("/tsconfig.json"), + projectId: Harness.mockHash("/tsconfig.json"), fileStats: fileStats({ ts: 1 }), compilerOptions: {}, extends: false, @@ -282,7 +282,7 @@ namespace ts.projectSystem { } function makeFile(path: string, content: {} = ""): projectSystem.FileOrFolder { - return { path, content: typeof content === "string" ? "" : JSON.stringify(content) }; + return { path, content: isString(content) ? "" : JSON.stringify(content) }; } function fileStats(nonZeroStats: Partial): server.FileStats { diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts new file mode 100644 index 0000000000..f5ed08b9b8 --- /dev/null +++ b/src/harness/unittests/tscWatchMode.ts @@ -0,0 +1,1657 @@ +/// +/// +/// + +namespace ts.tscWatch { + + import WatchedSystem = ts.TestFSWithWatch.TestServerHost; + type FileOrFolder = ts.TestFSWithWatch.FileOrFolder; + import createWatchedSystem = ts.TestFSWithWatch.createWatchedSystem; + import checkFileNames = ts.TestFSWithWatch.checkFileNames; + import libFile = ts.TestFSWithWatch.libFile; + import checkWatchedFiles = ts.TestFSWithWatch.checkWatchedFiles; + import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories; + import checkOutputContains = ts.TestFSWithWatch.checkOutputContains; + import checkOutputDoesNotContain = ts.TestFSWithWatch.checkOutputDoesNotContain; + + export function checkProgramActualFiles(program: Program, expectedFiles: string[]) { + checkFileNames(`Program actual files`, program.getSourceFiles().map(file => file.fileName), expectedFiles); + } + + export function checkProgramRootFiles(program: Program, expectedFiles: string[]) { + checkFileNames(`Program rootFileNames`, program.getRootFileNames(), expectedFiles); + } + + function createWatchingSystemHost(system: WatchedSystem) { + return ts.createWatchingSystemHost(/*pretty*/ undefined, system); + } + + function parseConfigFile(configFileName: string, watchingSystemHost: WatchingSystemHost) { + return ts.parseConfigFile(configFileName, {}, watchingSystemHost.system, watchingSystemHost.reportDiagnostic, watchingSystemHost.reportWatchDiagnostic); + } + + function createWatchModeWithConfigFile(configFilePath: string, host: WatchedSystem) { + const watchingSystemHost = createWatchingSystemHost(host); + const configFileResult = parseConfigFile(configFilePath, watchingSystemHost); + return ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost); + } + + function createWatchModeWithoutConfigFile(fileNames: string[], host: WatchedSystem, options: CompilerOptions = {}) { + const watchingSystemHost = createWatchingSystemHost(host); + return ts.createWatchModeWithoutConfigFile(fileNames, options, watchingSystemHost); + } + + function getEmittedLineForMultiFileOutput(file: FileOrFolder, host: WatchedSystem) { + return `TSFILE: ${file.path.replace(".ts", ".js")}${host.newLine}`; + } + + function getEmittedLineForSingleFileOutput(filename: string, host: WatchedSystem) { + return `TSFILE: ${filename}${host.newLine}`; + } + + interface FileOrFolderEmit extends FileOrFolder { + output?: string; + } + + function getFileOrFolderEmit(file: FileOrFolder, getOutput?: (file: FileOrFolder) => string): FileOrFolderEmit { + const result = file as FileOrFolderEmit; + if (getOutput) { + result.output = getOutput(file); + } + return result; + } + + function getEmittedLines(files: FileOrFolderEmit[]) { + const seen = createMap(); + const result: string[] = []; + for (const { output } of files) { + if (output && !seen.has(output)) { + seen.set(output, true); + result.push(output); + } + } + return result; + } + + function checkAffectedLines(host: WatchedSystem, affectedFiles: FileOrFolderEmit[], allEmittedFiles: string[]) { + const expectedAffectedFiles = getEmittedLines(affectedFiles); + const expectedNonAffectedFiles = mapDefined(allEmittedFiles, line => contains(expectedAffectedFiles, line) ? undefined : line); + checkOutputContains(host, expectedAffectedFiles); + checkOutputDoesNotContain(host, expectedNonAffectedFiles); + } + + describe("tsc-watch program updates", () => { + const commonFile1: FileOrFolder = { + path: "/a/b/commonFile1.ts", + content: "let x = 1" + }; + const commonFile2: FileOrFolder = { + path: "/a/b/commonFile2.ts", + content: "let y = 1" + }; + + it("create watch without config file", () => { + const appFile: FileOrFolder = { + path: "/a/b/c/app.ts", + content: ` + import {f} from "./module" + console.log(f) + ` + }; + + const moduleFile: FileOrFolder = { + path: "/a/b/c/module.d.ts", + content: `export let x: number` + }; + const host = createWatchedSystem([appFile, moduleFile, libFile]); + const watch = createWatchModeWithoutConfigFile([appFile.path], host); + + checkProgramActualFiles(watch(), [appFile.path, libFile.path, moduleFile.path]); + + // TODO: Should we watch creation of config files in the root file's file hierarchy? + + // const configFileLocations = ["/a/b/c/", "/a/b/", "/a/", "/"]; + // const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]); + // checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path)); + }); + + it("can handle tsconfig file name with difference casing", () => { + const f1 = { + path: "/a/b/app.ts", + content: "let x = 1" + }; + const config = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ + include: ["app.ts"] + }) + }; + + const host = createWatchedSystem([f1, config], { useCaseSensitiveFileNames: false }); + const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path)); + const watch = createWatchModeWithConfigFile(upperCaseConfigFilePath, host); + checkProgramActualFiles(watch(), [combinePaths(getDirectoryPath(upperCaseConfigFilePath), getBaseFileName(f1.path))]); + }); + + it("create configured project without file list", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: ` + { + "compilerOptions": {}, + "exclude": [ + "e" + ] + }` + }; + const file1: FileOrFolder = { + path: "/a/b/c/f1.ts", + content: "let x = 1" + }; + const file2: FileOrFolder = { + path: "/a/b/d/f2.ts", + content: "let y = 1" + }; + const file3: FileOrFolder = { + path: "/a/b/e/f3.ts", + content: "let z = 1" + }; + + const host = createWatchedSystem([configFile, libFile, file1, file2, file3]); + const watchingSystemHost = createWatchingSystemHost(host); + const configFileResult = parseConfigFile(configFile.path, watchingSystemHost); + assert.equal(configFileResult.errors.length, 0, `expect no errors in config file, got ${JSON.stringify(configFileResult.errors)}`); + + const watch = ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost); + + checkProgramActualFiles(watch(), [file1.path, libFile.path, file2.path]); + checkProgramRootFiles(watch(), [file1.path, file2.path]); + checkWatchedFiles(host, [configFile.path, file1.path, file2.path, libFile.path]); + checkWatchedDirectories(host, [getDirectoryPath(configFile.path)], /*recursive*/ true); + }); + + // TODO: if watching for config file creation + // it("add and then remove a config file in a folder with loose files", () => { + // }); + + it("add new files to a configured program without file list", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{}` + }; + const host = createWatchedSystem([commonFile1, libFile, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkWatchedDirectories(host, ["/a/b"], /*recursive*/ true); + + checkProgramRootFiles(watch(), [commonFile1.path]); + + // add a new ts file + host.reloadFS([commonFile1, commonFile2, libFile, configFile]); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); + }); + + it("should ignore non-existing files specified in the config file", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "files": [ + "commonFile1.ts", + "commonFile3.ts" + ] + }` + }; + const host = createWatchedSystem([commonFile1, commonFile2, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + + const commonFile3 = "/a/b/commonFile3.ts"; + checkProgramRootFiles(watch(), [commonFile1.path, commonFile3]); + checkProgramActualFiles(watch(), [commonFile1.path]); + }); + + it("handle recreated files correctly", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{}` + }; + const host = createWatchedSystem([commonFile1, commonFile2, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); + + // delete commonFile2 + host.reloadFS([commonFile1, configFile]); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramRootFiles(watch(), [commonFile1.path]); + + // re-add commonFile2 + host.reloadFS([commonFile1, commonFile2, configFile]); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); + }); + + it("handles the missing files - that were added to program because they were added with /// { + const file1: FileOrFolder = { + path: "/a/b/commonFile1.ts", + content: `/// + let x = y` + }; + const host = createWatchedSystem([file1, libFile]); + const watch = createWatchModeWithoutConfigFile([file1.path], host); + + checkProgramRootFiles(watch(), [file1.path]); + checkProgramActualFiles(watch(), [file1.path, libFile.path]); + const errors = [ + `a/b/commonFile1.ts(1,22): error TS6053: File '${commonFile2.path}' not found.${host.newLine}`, + `a/b/commonFile1.ts(2,29): error TS2304: Cannot find name 'y'.${host.newLine}` + ]; + checkOutputContains(host, errors); + host.clearOutput(); + + host.reloadFS([file1, commonFile2, libFile]); + host.runQueuedTimeoutCallbacks(); + checkProgramRootFiles(watch(), [file1.path]); + checkProgramActualFiles(watch(), [file1.path, libFile.path, commonFile2.path]); + checkOutputDoesNotContain(host, errors); + }); + + it("should reflect change in config file", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "files": ["${commonFile1.path}", "${commonFile2.path}"] + }` + }; + const files = [commonFile1, commonFile2, configFile]; + const host = createWatchedSystem(files); + const watch = createWatchModeWithConfigFile(configFile.path, host); + + checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); + configFile.content = `{ + "compilerOptions": {}, + "files": ["${commonFile1.path}"] + }`; + + host.reloadFS(files); + host.checkTimeoutQueueLengthAndRun(1); // reload the configured project + checkProgramRootFiles(watch(), [commonFile1.path]); + }); + + it("files explicitly excluded in config file", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "exclude": ["/a/c"] + }` + }; + const excludedFile1: FileOrFolder = { + path: "/a/c/excluedFile1.ts", + content: `let t = 1;` + }; + + const host = createWatchedSystem([commonFile1, commonFile2, excludedFile1, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); + }); + + it("should properly handle module resolution changes in config file", () => { + const file1: FileOrFolder = { + path: "/a/b/file1.ts", + content: `import { T } from "module1";` + }; + const nodeModuleFile: FileOrFolder = { + path: "/a/b/node_modules/module1.ts", + content: `export interface T {}` + }; + const classicModuleFile: FileOrFolder = { + path: "/a/module1.ts", + content: `export interface T {}` + }; + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": { + "moduleResolution": "node" + }, + "files": ["${file1.path}"] + }` + }; + const files = [file1, nodeModuleFile, classicModuleFile, configFile]; + const host = createWatchedSystem(files); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkProgramRootFiles(watch(), [file1.path]); + checkProgramActualFiles(watch(), [file1.path, nodeModuleFile.path]); + + configFile.content = `{ + "compilerOptions": { + "moduleResolution": "classic" + }, + "files": ["${file1.path}"] + }`; + host.reloadFS(files); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramRootFiles(watch(), [file1.path]); + checkProgramActualFiles(watch(), [file1.path, classicModuleFile.path]); + }); + + it("should tolerate config file errors and still try to build a project", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": { + "target": "es6", + "allowAnything": true + }, + "someOtherProperty": {} + }` + }; + const host = createWatchedSystem([commonFile1, commonFile2, libFile, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); + }); + + it("changes in files are reflected in project structure", () => { + const file1 = { + path: "/a/b/f1.ts", + content: `export * from "./f2"` + }; + const file2 = { + path: "/a/b/f2.ts", + content: `export let x = 1` + }; + const file3 = { + path: "/a/c/f3.ts", + content: `export let y = 1;` + }; + const host = createWatchedSystem([file1, file2, file3]); + const watch = createWatchModeWithoutConfigFile([file1.path], host); + checkProgramRootFiles(watch(), [file1.path]); + checkProgramActualFiles(watch(), [file1.path, file2.path]); + + const modifiedFile2 = { + path: file2.path, + content: `export * from "../c/f3"` // now inferred project should inclule file3 + }; + + host.reloadFS([file1, modifiedFile2, file3]); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramRootFiles(watch(), [file1.path]); + checkProgramActualFiles(watch(), [file1.path, modifiedFile2.path, file3.path]); + }); + + it("deleted files affect project structure", () => { + const file1 = { + path: "/a/b/f1.ts", + content: `export * from "./f2"` + }; + const file2 = { + path: "/a/b/f2.ts", + content: `export * from "../c/f3"` + }; + const file3 = { + path: "/a/c/f3.ts", + content: `export let y = 1;` + }; + const host = createWatchedSystem([file1, file2, file3]); + const watch = createWatchModeWithoutConfigFile([file1.path], host); + checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]); + + host.reloadFS([file1, file3]); + host.checkTimeoutQueueLengthAndRun(1); + + checkProgramActualFiles(watch(), [file1.path]); + }); + + it("deleted files affect project structure - 2", () => { + const file1 = { + path: "/a/b/f1.ts", + content: `export * from "./f2"` + }; + const file2 = { + path: "/a/b/f2.ts", + content: `export * from "../c/f3"` + }; + const file3 = { + path: "/a/c/f3.ts", + content: `export let y = 1;` + }; + const host = createWatchedSystem([file1, file2, file3]); + const watch = createWatchModeWithoutConfigFile([file1.path, file3.path], host); + checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]); + + host.reloadFS([file1, file3]); + host.checkTimeoutQueueLengthAndRun(1); + + checkProgramActualFiles(watch(), [file1.path, file3.path]); + }); + + it("config file includes the file", () => { + const file1 = { + path: "/a/b/f1.ts", + content: "export let x = 5" + }; + const file2 = { + path: "/a/c/f2.ts", + content: `import {x} from "../b/f1"` + }; + const file3 = { + path: "/a/c/f3.ts", + content: "export let y = 1" + }; + const configFile = { + path: "/a/c/tsconfig.json", + content: JSON.stringify({ compilerOptions: {}, files: ["f2.ts", "f3.ts"] }) + }; + + const host = createWatchedSystem([file1, file2, file3, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + + checkProgramRootFiles(watch(), [file2.path, file3.path]); + checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]); + }); + + it("correctly migrate files between projects", () => { + const file1 = { + path: "/a/b/f1.ts", + content: ` + export * from "../c/f2"; + export * from "../d/f3";` + }; + const file2 = { + path: "/a/c/f2.ts", + content: "export let x = 1;" + }; + const file3 = { + path: "/a/d/f3.ts", + content: "export let y = 1;" + }; + const host = createWatchedSystem([file1, file2, file3]); + const watch = createWatchModeWithoutConfigFile([file2.path, file3.path], host); + checkProgramActualFiles(watch(), [file2.path, file3.path]); + + const watch2 = createWatchModeWithoutConfigFile([file1.path], host); + checkProgramActualFiles(watch2(), [file1.path, file2.path, file3.path]); + + // Previous program shouldnt be updated + checkProgramActualFiles(watch(), [file2.path, file3.path]); + host.checkTimeoutQueueLength(0); + }); + + it("can correctly update configured project when set of root files has changed (new file on disk)", () => { + const file1 = { + path: "/a/b/f1.ts", + content: "let x = 1" + }; + const file2 = { + path: "/a/b/f2.ts", + content: "let y = 1" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ compilerOptions: {} }) + }; + + const host = createWatchedSystem([file1, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkProgramActualFiles(watch(), [file1.path]); + + host.reloadFS([file1, file2, configFile]); + host.checkTimeoutQueueLengthAndRun(1); + + checkProgramActualFiles(watch(), [file1.path, file2.path]); + checkProgramRootFiles(watch(), [file1.path, file2.path]); + }); + + it("can correctly update configured project when set of root files has changed (new file in list of files)", () => { + const file1 = { + path: "/a/b/f1.ts", + content: "let x = 1" + }; + const file2 = { + path: "/a/b/f2.ts", + content: "let y = 1" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts"] }) + }; + + const host = createWatchedSystem([file1, file2, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + + checkProgramActualFiles(watch(), [file1.path]); + + const modifiedConfigFile = { + path: configFile.path, + content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] }) + }; + + host.reloadFS([file1, file2, modifiedConfigFile]); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramRootFiles(watch(), [file1.path, file2.path]); + checkProgramActualFiles(watch(), [file1.path, file2.path]); + }); + + it("can update configured project when set of root files was not changed", () => { + const file1 = { + path: "/a/b/f1.ts", + content: "let x = 1" + }; + const file2 = { + path: "/a/b/f2.ts", + content: "let y = 1" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] }) + }; + + const host = createWatchedSystem([file1, file2, configFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkProgramActualFiles(watch(), [file1.path, file2.path]); + + const modifiedConfigFile = { + path: configFile.path, + content: JSON.stringify({ compilerOptions: { outFile: "out.js" }, files: ["f1.ts", "f2.ts"] }) + }; + + host.reloadFS([file1, file2, modifiedConfigFile]); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramRootFiles(watch(), [file1.path, file2.path]); + checkProgramActualFiles(watch(), [file1.path, file2.path]); + }); + + it("config file is deleted", () => { + const file1 = { + path: "/a/b/f1.ts", + content: "let x = 1;" + }; + const file2 = { + path: "/a/b/f2.ts", + content: "let y = 2;" + }; + const config = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ compilerOptions: {} }) + }; + const host = createWatchedSystem([file1, file2, config]); + const watch = createWatchModeWithConfigFile(config.path, host); + + checkProgramActualFiles(watch(), [file1.path, file2.path]); + + host.clearOutput(); + host.reloadFS([file1, file2]); + host.checkTimeoutQueueLengthAndRun(1); + + assert.equal(host.exitCode, ExitStatus.DiagnosticsPresent_OutputsSkipped); + checkOutputContains(host, [`error TS6053: File '${config.path}' not found.${host.newLine}`]); + }); + + it("Proper errors: document is not contained in project", () => { + const file1 = { + path: "/a/b/app.ts", + content: "" + }; + const corruptedConfig = { + path: "/a/b/tsconfig.json", + content: "{" + }; + const host = createWatchedSystem([file1, corruptedConfig]); + const watch = createWatchModeWithConfigFile(corruptedConfig.path, host); + + checkProgramActualFiles(watch(), [file1.path]); + }); + + it("correctly handles changes in lib section of config file", () => { + const libES5 = { + path: "/compiler/lib.es5.d.ts", + content: "declare const eval: any" + }; + const libES2015Promise = { + path: "/compiler/lib.es2015.promise.d.ts", + content: "declare class Promise {}" + }; + const app = { + path: "/src/app.ts", + content: "var x: Promise;" + }; + const config1 = { + path: "/src/tsconfig.json", + content: JSON.stringify( + { + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": true, + "sourceMap": false, + "lib": [ + "es5" + ] + } + }) + }; + const config2 = { + path: config1.path, + content: JSON.stringify( + { + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": true, + "sourceMap": false, + "lib": [ + "es5", + "es2015.promise" + ] + } + }) + }; + const host = createWatchedSystem([libES5, libES2015Promise, app, config1], { executingFilePath: "/compiler/tsc.js" }); + const watch = createWatchModeWithConfigFile(config1.path, host); + + checkProgramActualFiles(watch(), [libES5.path, app.path]); + + host.reloadFS([libES5, libES2015Promise, app, config2]); + host.checkTimeoutQueueLengthAndRun(1); + checkProgramActualFiles(watch(), [libES5.path, libES2015Promise.path, app.path]); + }); + + it("should handle non-existing directories in config file", () => { + const f = { + path: "/a/src/app.ts", + content: "let x = 1;" + }; + const config = { + path: "/a/tsconfig.json", + content: JSON.stringify({ + compilerOptions: {}, + include: [ + "src/**/*", + "notexistingfolder/*" + ] + }) + }; + const host = createWatchedSystem([f, config]); + const watch = createWatchModeWithConfigFile(config.path, host); + checkProgramActualFiles(watch(), [f.path]); + }); + + it("rename a module file and rename back should restore the states for inferred projects", () => { + const moduleFile = { + path: "/a/b/moduleFile.ts", + content: "export function bar() { };" + }; + const file1 = { + path: "/a/b/file1.ts", + content: "import * as T from './moduleFile'; T.bar();" + }; + const host = createWatchedSystem([moduleFile, file1, libFile]); + createWatchModeWithoutConfigFile([file1.path], host); + const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n"; + checkOutputDoesNotContain(host, [error]); + + const moduleFileOldPath = moduleFile.path; + const moduleFileNewPath = "/a/b/moduleFile1.ts"; + moduleFile.path = moduleFileNewPath; + host.reloadFS([moduleFile, file1, libFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputContains(host, [error]); + + host.clearOutput(); + moduleFile.path = moduleFileOldPath; + host.reloadFS([moduleFile, file1, libFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputDoesNotContain(host, [error]); + }); + + it("rename a module file and rename back should restore the states for configured projects", () => { + const moduleFile = { + path: "/a/b/moduleFile.ts", + content: "export function bar() { };" + }; + const file1 = { + path: "/a/b/file1.ts", + content: "import * as T from './moduleFile'; T.bar();" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: `{}` + }; + const host = createWatchedSystem([moduleFile, file1, configFile, libFile]); + createWatchModeWithConfigFile(configFile.path, host); + + const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n"; + checkOutputDoesNotContain(host, [error]); + + const moduleFileOldPath = moduleFile.path; + const moduleFileNewPath = "/a/b/moduleFile1.ts"; + moduleFile.path = moduleFileNewPath; + host.clearOutput(); + host.reloadFS([moduleFile, file1, configFile, libFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputContains(host, [error]); + + host.clearOutput(); + moduleFile.path = moduleFileOldPath; + host.reloadFS([moduleFile, file1, configFile, libFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputDoesNotContain(host, [error]); + }); + + it("types should load from config file path if config exists", () => { + const f1 = { + path: "/a/b/app.ts", + content: "let x = 1" + }; + const config = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ compilerOptions: { types: ["node"], typeRoots: [] } }) + }; + const node = { + path: "/a/b/node_modules/@types/node/index.d.ts", + content: "declare var process: any" + }; + const cwd = { + path: "/a/c" + }; + const host = createWatchedSystem([f1, config, node, cwd], { currentDirectory: cwd.path }); + const watch = createWatchModeWithConfigFile(config.path, host); + + checkProgramActualFiles(watch(), [f1.path, node.path]); + }); + + it("add the missing module file for inferred project: should remove the `module not found` error", () => { + const moduleFile = { + path: "/a/b/moduleFile.ts", + content: "export function bar() { };" + }; + const file1 = { + path: "/a/b/file1.ts", + content: "import * as T from './moduleFile'; T.bar();" + }; + const host = createWatchedSystem([file1, libFile]); + createWatchModeWithoutConfigFile([file1.path], host); + + const error = `a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.${host.newLine}`; + checkOutputContains(host, [error]); + host.clearOutput(); + + host.reloadFS([file1, moduleFile, libFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputDoesNotContain(host, [error]); + }); + + it("Configure file diagnostics events are generated when the config file has errors", () => { + const file = { + path: "/a/b/app.ts", + content: "let x = 10" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": { + "foo": "bar", + "allowJS": true + } + }` + }; + + const host = createWatchedSystem([file, configFile, libFile]); + createWatchModeWithConfigFile(configFile.path, host); + checkOutputContains(host, [ + `a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`, + `a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}` + ]); + }); + + it("Configure file diagnostics events are generated when the config file doesn't have errors", () => { + const file = { + path: "/a/b/app.ts", + content: "let x = 10" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {} + }` + }; + + const host = createWatchedSystem([file, configFile, libFile]); + createWatchModeWithConfigFile(configFile.path, host); + checkOutputDoesNotContain(host, [ + `a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`, + `a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}` + ]); + }); + + it("Configure file diagnostics events are generated when the config file changes", () => { + const file = { + path: "/a/b/app.ts", + content: "let x = 10" + }; + const configFile = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {} + }` + }; + + const host = createWatchedSystem([file, configFile, libFile]); + createWatchModeWithConfigFile(configFile.path, host); + const error = `a/b/tsconfig.json(3,25): error TS5023: Unknown compiler option 'haha'.${host.newLine}`; + checkOutputDoesNotContain(host, [error]); + + configFile.content = `{ + "compilerOptions": { + "haha": 123 + } + }`; + host.reloadFS([file, configFile, libFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputContains(host, [error]); + + host.clearOutput(); + configFile.content = `{ + "compilerOptions": {} + }`; + host.reloadFS([file, configFile, libFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputDoesNotContain(host, [error]); + }); + + it("non-existing directories listed in config file input array should be tolerated without crashing the server", () => { + const configFile = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "include": ["app/*", "test/**/*", "something"] + }` + }; + const file1 = { + path: "/a/b/file1.ts", + content: "let t = 10;" + }; + + const host = createWatchedSystem([file1, configFile, libFile]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + + checkProgramActualFiles(watch(), [libFile.path]); + }); + + it("non-existing directories listed in config file input array should be able to handle @types if input file list is empty", () => { + const f = { + path: "/a/app.ts", + content: "let x = 1" + }; + const config = { + path: "/a/tsconfig.json", + content: JSON.stringify({ + compiler: {}, + files: [] + }) + }; + const t1 = { + path: "/a/node_modules/@types/typings/index.d.ts", + content: `export * from "./lib"` + }; + const t2 = { + path: "/a/node_modules/@types/typings/lib.d.ts", + content: `export const x: number` + }; + const host = createWatchedSystem([f, config, t1, t2], { currentDirectory: getDirectoryPath(f.path) }); + const watch = createWatchModeWithConfigFile(config.path, host); + + checkProgramActualFiles(watch(), [t1.path, t2.path]); + }); + + it("should support files without extensions", () => { + const f = { + path: "/a/compile", + content: "let x = 1" + }; + const host = createWatchedSystem([f, libFile]); + const watch = createWatchModeWithoutConfigFile([f.path], host, { allowNonTsExtensions: true }); + checkProgramActualFiles(watch(), [f.path, libFile.path]); + }); + + it("Options Diagnostic locations reported correctly with changes in configFile contents when options change", () => { + const file = { + path: "/a/b/app.ts", + content: "let x = 10" + }; + const configFileContentBeforeComment = `{`; + const configFileContentComment = ` + // comment + // More comment`; + const configFileContentAfterComment = ` + "compilerOptions": { + "allowJs": true, + "declaration": true + } + }`; + const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment; + const configFileContentWithoutCommentLine = configFileContentBeforeComment + configFileContentAfterComment; + + const line = 5; + const errors = (line: number) => [ + `a/b/tsconfig.json(${line},25): error TS5053: Option \'allowJs\' cannot be specified with option \'declaration\'.\n`, + `a/b/tsconfig.json(${line + 1},25): error TS5053: Option \'allowJs\' cannot be specified with option \'declaration\'.\n` + ]; + + const configFile = { + path: "/a/b/tsconfig.json", + content: configFileContentWithComment + }; + + const host = createWatchedSystem([file, libFile, configFile]); + createWatchModeWithConfigFile(configFile.path, host); + checkOutputContains(host, errors(line)); + checkOutputDoesNotContain(host, errors(line - 2)); + host.clearOutput(); + + configFile.content = configFileContentWithoutCommentLine; + host.reloadFS([file, configFile]); + host.runQueuedTimeoutCallbacks(); + checkOutputContains(host, errors(line - 2)); + checkOutputDoesNotContain(host, errors(line)); + }); + }); + + describe("tsc-watch emit with outFile or out setting", () => { + function createWatchForOut(out?: string, outFile?: string) { + const host = createWatchedSystem([]); + const config: FileOrFolderEmit = { + path: "/a/tsconfig.json", + content: JSON.stringify({ + compilerOptions: { listEmittedFiles: true } + }) + }; + + let getOutput: (file: FileOrFolder) => string; + if (out) { + config.content = JSON.stringify({ + compilerOptions: { listEmittedFiles: true, out } + }); + getOutput = __ => getEmittedLineForSingleFileOutput(out, host); + } + else if (outFile) { + config.content = JSON.stringify({ + compilerOptions: { listEmittedFiles: true, outFile } + }); + getOutput = __ => getEmittedLineForSingleFileOutput(outFile, host); + } + else { + getOutput = file => getEmittedLineForMultiFileOutput(file, host); + } + + const f1 = getFileOrFolderEmit({ + path: "/a/a.ts", + content: "let x = 1" + }, getOutput); + const f2 = getFileOrFolderEmit({ + path: "/a/b.ts", + content: "let y = 1" + }, getOutput); + + const files = [f1, f2, config, libFile]; + host.reloadFS(files); + createWatchModeWithConfigFile(config.path, host); + + const allEmittedLines = getEmittedLines(files); + checkOutputContains(host, allEmittedLines); + host.clearOutput(); + + f1.content = "let x = 11"; + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + checkAffectedLines(host, [f1], allEmittedLines); + } + + it("projectUsesOutFile should not be returned if not set", () => { + createWatchForOut(); + }); + + it("projectUsesOutFile should be true if out is set", () => { + const outJs = "/a/out.js"; + createWatchForOut(outJs); + }); + + it("projectUsesOutFile should be true if outFile is set", () => { + const outJs = "/a/out.js"; + createWatchForOut(/*out*/ undefined, outJs); + }); + }); + + describe("tsc-watch emit for configured projects", () => { + const file1Consumer1Path = "/a/b/file1Consumer1.ts"; + const moduleFile1Path = "/a/b/moduleFile1.ts"; + const configFilePath = "/a/b/tsconfig.json"; + type InitialStateParams = { + /** custom config file options */ + configObj?: any; + /** list of the files that will be emitted for first compilation */ + firstCompilationEmitFiles?: string[]; + /** get the emit file for file - default is multi file emit line */ + getEmitLine?(file: FileOrFolder, host: WatchedSystem): string; + /** Additional files and folders to add */ + getAdditionalFileOrFolder?(): FileOrFolder[]; + /** initial list of files to emit if not the default list */ + firstReloadFileList?: string[]; + }; + function getInitialState({ configObj = {}, firstCompilationEmitFiles, getEmitLine, getAdditionalFileOrFolder, firstReloadFileList }: InitialStateParams = {}) { + const host = createWatchedSystem([]); + const getOutputName = getEmitLine ? (file: FileOrFolder) => getEmitLine(file, host) : + (file: FileOrFolder) => getEmittedLineForMultiFileOutput(file, host); + + const moduleFile1 = getFileOrFolderEmit({ + path: moduleFile1Path, + content: "export function Foo() { };", + }, getOutputName); + + const file1Consumer1 = getFileOrFolderEmit({ + path: file1Consumer1Path, + content: `import {Foo} from "./moduleFile1"; export var y = 10;`, + }, getOutputName); + + const file1Consumer2 = getFileOrFolderEmit({ + path: "/a/b/file1Consumer2.ts", + content: `import {Foo} from "./moduleFile1"; let z = 10;`, + }, getOutputName); + + const moduleFile2 = getFileOrFolderEmit({ + path: "/a/b/moduleFile2.ts", + content: `export var Foo4 = 10;`, + }, getOutputName); + + const globalFile3 = getFileOrFolderEmit({ + path: "/a/b/globalFile3.ts", + content: `interface GlobalFoo { age: number }` + }); + + const additionalFiles = getAdditionalFileOrFolder ? + map(getAdditionalFileOrFolder(), file => getFileOrFolderEmit(file, getOutputName)) : + []; + + (configObj.compilerOptions || (configObj.compilerOptions = {})).listEmittedFiles = true; + const configFile = getFileOrFolderEmit({ + path: configFilePath, + content: JSON.stringify(configObj) + }); + + const files = [moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile, ...additionalFiles]; + let allEmittedFiles = getEmittedLines(files); + host.reloadFS(firstReloadFileList ? getFiles(firstReloadFileList) : files); + + // Initial compile + createWatchModeWithConfigFile(configFile.path, host); + if (firstCompilationEmitFiles) { + checkAffectedLines(host, getFiles(firstCompilationEmitFiles), allEmittedFiles); + } + else { + checkOutputContains(host, allEmittedFiles); + } + host.clearOutput(); + + return { + moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile, + files, + getFile, + verifyAffectedFiles, + verifyAffectedAllFiles, + getOutputName + }; + + function getFiles(filelist: string[]) { + return map(filelist, getFile); + } + + function getFile(fileName: string) { + return find(files, file => file.path === fileName); + } + + function verifyAffectedAllFiles() { + host.reloadFS(files); + host.checkTimeoutQueueLengthAndRun(1); + checkOutputContains(host, allEmittedFiles); + host.clearOutput(); + } + + function verifyAffectedFiles(expected: FileOrFolderEmit[], filesToReload?: FileOrFolderEmit[]) { + if (!filesToReload) { + filesToReload = files; + } + else if (filesToReload.length > files.length) { + allEmittedFiles = getEmittedLines(filesToReload); + } + host.reloadFS(filesToReload); + host.checkTimeoutQueueLengthAndRun(1); + checkAffectedLines(host, expected, allEmittedFiles); + host.clearOutput(); + } + } + + it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => { + const { + moduleFile1, file1Consumer1, file1Consumer2, + verifyAffectedFiles + } = getInitialState(); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer1, file1Consumer2]); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { console.log('hi'); };` + moduleFile1.content = `export var T: number;export function Foo() { console.log('hi'); };`; + verifyAffectedFiles([moduleFile1]); + }); + + it("should be up-to-date with the reference map changes", () => { + const { + moduleFile1, file1Consumer1, file1Consumer2, + verifyAffectedFiles + } = getInitialState(); + + // Change file1Consumer1 content to `export let y = Foo();` + file1Consumer1.content = `export let y = Foo();`; + verifyAffectedFiles([file1Consumer1]); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer2]); + + // Add the import statements back to file1Consumer1 + file1Consumer1.content = `import {Foo} from "./moduleFile1";let y = Foo();`; + verifyAffectedFiles([file1Consumer1]); + + // Change the content of moduleFile1 to `export var T: number;export var T2: string;export function Foo() { };` + moduleFile1.content = `export var T: number;export var T2: string;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer2, file1Consumer1]); + + // Multiple file edits in one go: + + // Change file1Consumer1 content to `export let y = Foo();` + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + file1Consumer1.content = `export let y = Foo();`; + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer1, file1Consumer2]); + }); + + it("should be up-to-date with deleted files", () => { + const { + moduleFile1, file1Consumer1, file1Consumer2, + files, + verifyAffectedFiles + } = getInitialState(); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + moduleFile1.content = `export var T: number;export function Foo() { };`; + + // Delete file1Consumer2 + const filesToLoad = mapDefined(files, file => file === file1Consumer2 ? undefined : file); + verifyAffectedFiles([moduleFile1, file1Consumer1], filesToLoad); + }); + + it("should be up-to-date with newly created files", () => { + const { + moduleFile1, file1Consumer1, file1Consumer2, + files, + verifyAffectedFiles, + getOutputName + } = getInitialState(); + + const file1Consumer3 = getFileOrFolderEmit({ + path: "/a/b/file1Consumer3.ts", + content: `import {Foo} from "./moduleFile1"; let y = Foo();` + }, getOutputName); + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer1, file1Consumer3, file1Consumer2], files.concat(file1Consumer3)); + }); + + it("should detect changes in non-root files", () => { + const { + moduleFile1, file1Consumer1, + verifyAffectedFiles + } = getInitialState({ configObj: { files: [file1Consumer1Path] }, firstCompilationEmitFiles: [file1Consumer1Path, moduleFile1Path] }); + + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer1]); + + // change file1 internal, and verify only file1 is affected + moduleFile1.content += "var T1: number;"; + verifyAffectedFiles([moduleFile1]); + }); + + it("should return all files if a global file changed shape", () => { + const { + globalFile3, verifyAffectedAllFiles + } = getInitialState(); + + globalFile3.content += "var T2: string;"; + verifyAffectedAllFiles(); + }); + + it("should always return the file itself if '--isolatedModules' is specified", () => { + const { + moduleFile1, verifyAffectedFiles + } = getInitialState({ configObj: { compilerOptions: { isolatedModules: true } } }); + + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1]); + }); + + it("should always return the file itself if '--out' or '--outFile' is specified", () => { + const outFilePath = "/a/b/out.js"; + const { + moduleFile1, verifyAffectedFiles + } = getInitialState({ + configObj: { compilerOptions: { module: "system", outFile: outFilePath } }, + getEmitLine: (_, host) => getEmittedLineForSingleFileOutput(outFilePath, host) + }); + + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1]); + }); + + it("should return cascaded affected file list", () => { + const file1Consumer1Consumer1: FileOrFolder = { + path: "/a/b/file1Consumer1Consumer1.ts", + content: `import {y} from "./file1Consumer1";` + }; + const { + moduleFile1, file1Consumer1, file1Consumer2, verifyAffectedFiles, getFile + } = getInitialState({ + getAdditionalFileOrFolder: () => [file1Consumer1Consumer1] + }); + + const file1Consumer1Consumer1Emit = getFile(file1Consumer1Consumer1.path); + file1Consumer1.content += "export var T: number;"; + verifyAffectedFiles([file1Consumer1, file1Consumer1Consumer1Emit]); + + // Doesnt change the shape of file1Consumer1 + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer1, file1Consumer2]); + + // Change both files before the timeout + file1Consumer1.content += "export var T2: number;"; + moduleFile1.content = `export var T2: number;export function Foo() { };`; + verifyAffectedFiles([moduleFile1, file1Consumer1, file1Consumer2, file1Consumer1Consumer1Emit]); + }); + + it("should work fine for files with circular references", () => { + // TODO: do not exit on such errors? Just continue to watch the files for update in watch mode + + const file1: FileOrFolder = { + path: "/a/b/file1.ts", + content: ` + /// + export var t1 = 10;` + }; + const file2: FileOrFolder = { + path: "/a/b/file2.ts", + content: ` + /// + export var t2 = 10;` + }; + const { + configFile, + getFile, + verifyAffectedFiles + } = getInitialState({ + firstCompilationEmitFiles: [file1.path, file2.path], + getAdditionalFileOrFolder: () => [file1, file2], + firstReloadFileList: [libFile.path, file1.path, file2.path, configFilePath] + }); + const file1Emit = getFile(file1.path), file2Emit = getFile(file2.path); + + file1Emit.content += "export var t3 = 10;"; + verifyAffectedFiles([file1Emit, file2Emit], [file1, file2, libFile, configFile]); + + }); + + it("should detect removed code file", () => { + const referenceFile1: FileOrFolder = { + path: "/a/b/referenceFile1.ts", + content: ` + /// + export var x = Foo();` + }; + const { + configFile, + getFile, + verifyAffectedFiles + } = getInitialState({ + firstCompilationEmitFiles: [referenceFile1.path, moduleFile1Path], + getAdditionalFileOrFolder: () => [referenceFile1], + firstReloadFileList: [libFile.path, referenceFile1.path, moduleFile1Path, configFilePath] + }); + + const referenceFile1Emit = getFile(referenceFile1.path); + verifyAffectedFiles([referenceFile1Emit], [libFile, referenceFile1Emit, configFile]); + }); + + it("should detect non-existing code file", () => { + const referenceFile1: FileOrFolder = { + path: "/a/b/referenceFile1.ts", + content: ` + /// + export var x = Foo();` + }; + const { + configFile, + moduleFile2, + getFile, + verifyAffectedFiles + } = getInitialState({ + firstCompilationEmitFiles: [referenceFile1.path], + getAdditionalFileOrFolder: () => [referenceFile1], + firstReloadFileList: [libFile.path, referenceFile1.path, configFilePath] + }); + + const referenceFile1Emit = getFile(referenceFile1.path); + referenceFile1Emit.content += "export var yy = Foo();"; + verifyAffectedFiles([referenceFile1Emit], [libFile, referenceFile1Emit, configFile]); + + // Create module File2 and see both files are saved + verifyAffectedFiles([referenceFile1Emit, moduleFile2], [libFile, moduleFile2, referenceFile1Emit, configFile]); + }); + }); + + describe("tsc-watch emit file content", () => { + interface EmittedFile extends FileOrFolder { + shouldBeWritten: boolean; + } + function getEmittedFiles(files: FileOrFolderEmit[], contents: string[]): EmittedFile[] { + return map(contents, (content, index) => { + return { + content, + path: changeExtension(files[index].path, Extension.Js), + shouldBeWritten: true + }; + } + ); + } + function verifyEmittedFiles(host: WatchedSystem, emittedFiles: EmittedFile[]) { + for (const { path, content, shouldBeWritten } of emittedFiles) { + if (shouldBeWritten) { + assert.isTrue(host.fileExists(path), `Expected file ${path} to be present`); + assert.equal(host.readFile(path), content, `Contents of file ${path} do not match`); + } + else { + assert.isNotTrue(host.fileExists(path), `Expected file ${path} to be absent`); + } + } + } + + function verifyEmittedFileContents(newLine: string, inputFiles: FileOrFolder[], initialEmittedFileContents: string[], + modifyFiles: (files: FileOrFolderEmit[], emitedFiles: EmittedFile[]) => FileOrFolderEmit[], configFile?: FileOrFolder) { + const host = createWatchedSystem([], { newLine }); + const files = concatenate( + map(inputFiles, file => getFileOrFolderEmit(file, fileToConvert => getEmittedLineForMultiFileOutput(fileToConvert, host))), + configFile ? [libFile, configFile] : [libFile] + ); + const allEmittedFiles = getEmittedLines(files); + host.reloadFS(files); + + // Initial compile + if (configFile) { + createWatchModeWithConfigFile(configFile.path, host); + } + else { + // First file as the root + createWatchModeWithoutConfigFile([files[0].path], host, { listEmittedFiles: true }); + } + checkOutputContains(host, allEmittedFiles); + + const emittedFiles = getEmittedFiles(files, initialEmittedFileContents); + verifyEmittedFiles(host, emittedFiles); + host.clearOutput(); + + const affectedFiles = modifyFiles(files, emittedFiles); + host.reloadFS(files); + host.checkTimeoutQueueLengthAndRun(1); + checkAffectedLines(host, affectedFiles, allEmittedFiles); + + verifyEmittedFiles(host, emittedFiles); + } + + function verifyNewLine(newLine: string) { + const lines = ["var x = 1;", "var y = 2;"]; + const fileContent = lines.join(newLine); + const f = { + path: "/a/app.ts", + content: fileContent + }; + + verifyEmittedFileContents(newLine, [f], [fileContent + newLine], modifyFiles); + + function modifyFiles(files: FileOrFolderEmit[], emittedFiles: EmittedFile[]) { + files[0].content = fileContent + newLine + "var z = 3;"; + emittedFiles[0].content = files[0].content + newLine; + return [files[0]]; + } + } + + it("handles new lines: \\n", () => { + verifyNewLine("\n"); + }); + + it("handles new lines: \\r\\n", () => { + verifyNewLine("\r\n"); + }); + + it("should emit specified file", () => { + const file1 = { + path: "/a/b/f1.ts", + content: `export function Foo() { return 10; }` + }; + + const file2 = { + path: "/a/b/f2.ts", + content: `import {Foo} from "./f1"; export let y = Foo();` + }; + + const file3 = { + path: "/a/b/f3.ts", + content: `import {y} from "./f2"; let x = y;` + }; + + const configFile = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ compilerOptions: { listEmittedFiles: true } }) + }; + + verifyEmittedFileContents("\r\n", [file1, file2, file3], [ + `"use strict";\r\nexports.__esModule = true;\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`, + `"use strict";\r\nexports.__esModule = true;\r\nvar f1_1 = require("./f1");\r\nexports.y = f1_1.Foo();\r\n`, + `"use strict";\r\nexports.__esModule = true;\r\nvar f2_1 = require("./f2");\r\nvar x = f2_1.y;\r\n` + ], modifyFiles, configFile); + + function modifyFiles(files: FileOrFolderEmit[], emittedFiles: EmittedFile[]) { + files[0].content += `export function foo2() { return 2; }`; + emittedFiles[0].content += `function foo2() { return 2; }\r\nexports.foo2 = foo2;\r\n`; + emittedFiles[2].shouldBeWritten = false; + return files.slice(0, 2); + } + }); + }); + + describe("tsc-watch module resolution caching", () => { + it("works", () => { + const root = { + path: "/a/d/f0.ts", + content: `import {x} from "f1"` + }; + + const imported = { + path: "/a/f1.ts", + content: `foo()` + }; + + const f1IsNotModule = `a/d/f0.ts(1,17): error TS2306: File '${imported.path}' is not a module.\n`; + const cannotFindFoo = `a/f1.ts(1,1): error TS2304: Cannot find name 'foo'.\n`; + const cannotAssignValue = "a/d/f0.ts(2,21): error TS2322: Type '1' is not assignable to type 'string'.\n"; + + const files = [root, imported, libFile]; + const host = createWatchedSystem(files); + createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + + // ensure that imported file was found + checkOutputContains(host, [f1IsNotModule, cannotFindFoo]); + host.clearOutput(); + + const originalFileExists = host.fileExists; + { + const newContent = `import {x} from "f1" + var x: string = 1;`; + root.content = newContent; + host.reloadFS(files); + + // patch fileExists to make sure that disk is not touched + host.fileExists = notImplemented; + + // trigger synchronization to make sure that import will be fetched from the cache + host.runQueuedTimeoutCallbacks(); + + // ensure file has correct number of errors after edit + checkOutputContains(host, [f1IsNotModule, cannotAssignValue]); + host.clearOutput(); + } + { + let fileExistsIsCalled = false; + host.fileExists = (fileName): boolean => { + if (fileName === "lib.d.ts") { + return false; + } + fileExistsIsCalled = true; + assert.isTrue(fileName.indexOf("/f2.") !== -1); + return originalFileExists.call(host, fileName); + }; + + root.content = `import {x} from "f2"`; + host.reloadFS(files); + + // trigger synchronization to make sure that LSHost will try to find 'f2' module on disk + host.runQueuedTimeoutCallbacks(); + + // ensure file has correct number of errors after edit + const cannotFindModuleF2 = `a/d/f0.ts(1,17): error TS2307: Cannot find module 'f2'.\n`; + checkOutputContains(host, [cannotFindModuleF2]); + host.clearOutput(); + + assert.isTrue(fileExistsIsCalled); + } + { + let fileExistsCalled = false; + host.fileExists = (fileName): boolean => { + if (fileName === "lib.d.ts") { + return false; + } + fileExistsCalled = true; + assert.isTrue(fileName.indexOf("/f1.") !== -1); + return originalFileExists.call(host, fileName); + }; + + const newContent = `import {x} from "f1"`; + root.content = newContent; + + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + + checkOutputContains(host, [f1IsNotModule, cannotFindFoo]); + assert.isTrue(fileExistsCalled); + } + }); + + it("loads missing files from disk", () => { + const root = { + path: `/a/foo.ts`, + content: `import {x} from "bar"` + }; + + const imported = { + path: `/a/bar.d.ts`, + content: `export const y = 1;` + }; + + const files = [root, libFile]; + const host = createWatchedSystem(files); + const originalFileExists = host.fileExists; + + let fileExistsCalledForBar = false; + host.fileExists = fileName => { + if (fileName === "lib.d.ts") { + return false; + } + if (!fileExistsCalledForBar) { + fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1; + } + + return originalFileExists.call(host, fileName); + }; + + createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + + const barNotFound = `a/foo.ts(1,17): error TS2307: Cannot find module 'bar'.\n`; + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); + checkOutputContains(host, [barNotFound]); + host.clearOutput(); + + fileExistsCalledForBar = false; + root.content = `import {y} from "bar"`; + host.reloadFS(files.concat(imported)); + + host.runQueuedTimeoutCallbacks(); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); + checkOutputDoesNotContain(host, [barNotFound]); + }); + + it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => { + const root = { + path: `/a/foo.ts`, + content: `import {x} from "bar"` + }; + + const imported = { + path: `/a/bar.d.ts`, + content: `export const y = 1;` + }; + + const files = [root, libFile]; + const filesWithImported = files.concat(imported); + const host = createWatchedSystem(filesWithImported); + const originalFileExists = host.fileExists; + let fileExistsCalledForBar = false; + host.fileExists = fileName => { + if (fileName === "lib.d.ts") { + return false; + } + if (!fileExistsCalledForBar) { + fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1; + } + return originalFileExists.call(host, fileName); + }; + + createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + + const barNotFound = `a/foo.ts(1,17): error TS2307: Cannot find module 'bar'.\n`; + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); + checkOutputDoesNotContain(host, [barNotFound]); + host.clearOutput(); + + fileExistsCalledForBar = false; + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); + checkOutputContains(host, [barNotFound]); + host.clearOutput(); + + fileExistsCalledForBar = false; + host.reloadFS(filesWithImported); + host.checkTimeoutQueueLengthAndRun(1); + assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); + checkOutputDoesNotContain(host, [barNotFound]); + }); + }); +} diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index c28f1f5fa8..3c394da4e3 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1,4 +1,5 @@ /// +/// /// namespace ts.projectSystem { @@ -6,17 +7,14 @@ namespace ts.projectSystem { import protocol = server.protocol; import CommandNames = server.CommandNames; - const safeList = { - path: "/safeList.json", - content: JSON.stringify({ - commander: "commander", - express: "express", - jquery: "jquery", - lodash: "lodash", - moment: "moment", - chroma: "chroma-js" - }) - }; + export import TestServerHost = ts.TestFSWithWatch.TestServerHost; + export type FileOrFolder = ts.TestFSWithWatch.FileOrFolder; + export import createServerHost = ts.TestFSWithWatch.createServerHost; + export import checkFileNames = ts.TestFSWithWatch.checkFileNames; + export import libFile = ts.TestFSWithWatch.libFile; + export import checkWatchedFiles = ts.TestFSWithWatch.checkWatchedFiles; + import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories; + import safeList = ts.TestFSWithWatch.safeList; const customSafeList = { path: "/typeMapList.json", @@ -45,12 +43,6 @@ namespace ts.projectSystem { getLogFileName: (): string => undefined }; - const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); - export const libFile: FileOrFolder = { - path: "/a/lib/lib.d.ts", - content: libFileContent - }; - export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller { protected projectService: server.ProjectService; constructor( @@ -101,7 +93,7 @@ namespace ts.projectSystem { } addPostExecAction(stdout: string | string[], cb: TI.RequestCompletedAction) { - const out = typeof stdout === "string" ? stdout : createNpmPackageJsonString(stdout); + const out = isString(stdout) ? stdout : createNpmPackageJsonString(stdout); const action: PostExecAction = { success: !!out, callback: cb @@ -118,10 +110,6 @@ namespace ts.projectSystem { return JSON.stringify({ dependencies }); } - function getExecutingFilePathFromLibFile(): string { - return combinePaths(getDirectoryPath(libFile.path), "tsc.js"); - } - export function toExternalFile(fileName: string): protocol.ExternalFile { return { fileName }; } @@ -137,32 +125,12 @@ namespace ts.projectSystem { this.events.push(event); } - checkEventCountOfType(eventType: "context" | "configFileDiag", expectedCount: number) { + checkEventCountOfType(eventType: "configFileDiag", expectedCount: number) { const eventsOfType = filter(this.events, e => e.eventName === eventType); assert.equal(eventsOfType.length, expectedCount, `The actual event counts of type ${eventType} is ${eventsOfType.length}, while expected ${expectedCount}`); } } - interface TestServerHostCreationParameters { - useCaseSensitiveFileNames?: boolean; - executingFilePath?: string; - currentDirectory?: string; - newLine?: string; - } - - export function createServerHost(fileOrFolderList: FileOrFolder[], params?: TestServerHostCreationParameters): TestServerHost { - if (!params) { - params = {}; - } - const host = new TestServerHost( - params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false, - params.executingFilePath || getExecutingFilePathFromLibFile(), - params.currentDirectory || "/", - fileOrFolderList, - params.newLine); - return host; - } - class TestSession extends server.Session { private seq = 0; @@ -218,7 +186,6 @@ namespace ts.projectSystem { eventHandler?: server.ProjectServiceEventHandler; } - export class TestProjectService extends server.ProjectService { constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean, typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler, opts: Partial = {}) { @@ -245,60 +212,7 @@ namespace ts.projectSystem { return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler); } - export interface FileOrFolder { - path: string; - content?: string; - fileSize?: number; - } - - export interface FSEntry { - path: Path; - fullPath: string; - } - - export interface File extends FSEntry { - content: string; - fileSize?: number; - } - - export interface Folder extends FSEntry { - entries: FSEntry[]; - } - - function isFolder(s: FSEntry | undefined): s is Folder { - return s && isArray((s).entries); - } - - function isFile(s: FSEntry | undefined): s is File { - return s && typeof (s).content === "string"; - } - - function invokeWatcherCallbacks(callbacks: T[], invokeCallback: (cb: T) => void): void { - if (callbacks) { - // The array copy is made to ensure that even if one of the callback removes the callbacks, - // we dont miss any callbacks following it - const cbs = callbacks.slice(); - for (const cb of cbs) { - invokeCallback(cb); - } - } - } - - function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { - assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}`); - for (const name of expectedKeys) { - assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`); - } - } - - function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { - assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`); - for (const f of expectedFileNames) { - assert.isTrue(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${JSON.stringify(actualFileNames)}`); - } - } - - function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) { + export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.configuredProjects.size, expected, `expected ${expected} configured project(s)`); } @@ -325,14 +239,6 @@ namespace ts.projectSystem { return values.next().value; } - export function checkWatchedFiles(host: TestServerHost, expectedFiles: string[]) { - checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles); - } - - function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[], recursive = false) { - checkMapKeys("watchedDirectories", recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories); - } - export function checkProjectActualFiles(project: server.Project, expectedFiles: string[]) { checkFileNames(`${server.ProjectKind[project.projectKind]} project, actual files`, project.getFileNames(), expectedFiles); } @@ -341,400 +247,6 @@ namespace ts.projectSystem { checkFileNames(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles); } - class Callbacks { - private map: TimeOutCallback[] = []; - private nextId = 1; - - register(cb: (...args: any[]) => void, args: any[]) { - const timeoutId = this.nextId; - this.nextId++; - this.map[timeoutId] = cb.bind(/*this*/ undefined, ...args); - return timeoutId; - } - - unregister(id: any) { - if (typeof id === "number") { - delete this.map[id]; - } - } - - count() { - let n = 0; - for (const _ in this.map) { - n++; - } - return n; - } - - invoke() { - // Note: invoking a callback may result in new callbacks been queued, - // so do not clear the entire callback list regardless. Only remove the - // ones we have invoked. - for (const key in this.map) { - this.map[key](); - delete this.map[key]; - } - } - } - - type TimeOutCallback = () => any; - - export class TestServerHost implements server.ServerHost { - args: string[] = []; - - private readonly output: string[] = []; - - private fs = createMap(); - private getCanonicalFileName: (s: string) => string; - private toPath: (f: string) => Path; - private timeoutCallbacks = new Callbacks(); - private immediateCallbacks = new Callbacks(); - - readonly watchedDirectories = createMultiMap(); - readonly watchedDirectoriesRecursive = createMultiMap(); - readonly watchedFiles = createMultiMap(); - - constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[], public readonly newLine = "\n") { - this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); - this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName); - - this.reloadFS(fileOrFolderList); - } - - private toFullPath(s: string) { - const fullPath = getNormalizedAbsolutePath(s, this.currentDirectory); - return this.toPath(fullPath); - } - - reloadFS(fileOrFolderList: FileOrFolder[]) { - const mapNewLeaves = createMap(); - const isNewFs = this.fs.size === 0; - // always inject safelist file in the list of files - for (const fileOrFolder of fileOrFolderList.concat(safeList)) { - const path = this.toFullPath(fileOrFolder.path); - mapNewLeaves.set(path, true); - // If its a change - const currentEntry = this.fs.get(path); - if (currentEntry) { - if (isFile(currentEntry)) { - if (typeof fileOrFolder.content === "string") { - // Update file - if (currentEntry.content !== fileOrFolder.content) { - currentEntry.content = fileOrFolder.content; - currentEntry.fileSize = fileOrFolder.fileSize; - this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed); - } - } - else { - // TODO: Changing from file => folder - Debug.fail(`Currently ${path} is file and new FS makes it folder which isnt supported yet`); - } - } - else { - // Folder - if (typeof fileOrFolder.content === "string") { - // TODO: Changing from folder => file - Debug.fail(`Currently ${path} is folder and new FS makes it file which isnt supported yet`); - } - else { - // Folder update: Nothing to do. - } - } - } - else { - this.ensureFileOrFolder(fileOrFolder); - } - } - - if (!isNewFs) { - this.fs.forEach((fileOrFolder, path) => { - // If this entry is not from the new file or folder - if (!mapNewLeaves.get(path)) { - // Leaf entries that arent in new list => remove these - if (isFile(fileOrFolder) || isFolder(fileOrFolder) && fileOrFolder.entries.length === 0) { - this.removeFileOrFolder(fileOrFolder, folder => !mapNewLeaves.get(folder.path)); - } - } - }); - } - } - - ensureFileOrFolder(fileOrFolder: FileOrFolder) { - if (typeof fileOrFolder.content === "string") { - const file = this.toFile(fileOrFolder); - Debug.assert(!this.fs.get(file.path)); - const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath)); - this.addFileOrFolderInFolder(baseFolder, file); - } - else { - const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory); - this.ensureFolder(fullPath); - } - } - - private ensureFolder(fullPath: string): Folder { - const path = this.toPath(fullPath); - let folder = this.fs.get(path) as Folder; - if (!folder) { - folder = this.toFolder(fullPath); - const baseFullPath = getDirectoryPath(fullPath); - if (fullPath !== baseFullPath) { - // Add folder in the base folder - const baseFolder = this.ensureFolder(baseFullPath); - this.addFileOrFolderInFolder(baseFolder, folder); - } - else { - // root folder - Debug.assert(this.fs.size === 0); - this.fs.set(path, folder); - } - } - Debug.assert(isFolder(folder)); - return folder; - } - - private addFileOrFolderInFolder(folder: Folder, fileOrFolder: File | Folder) { - folder.entries.push(fileOrFolder); - this.fs.set(fileOrFolder.path, fileOrFolder); - - if (isFile(fileOrFolder)) { - this.invokeFileWatcher(fileOrFolder.fullPath, FileWatcherEventKind.Created); - } - this.invokeDirectoryWatcher(folder.fullPath, fileOrFolder.fullPath); - } - - private removeFileOrFolder(fileOrFolder: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean) { - const basePath = getDirectoryPath(fileOrFolder.path); - const baseFolder = this.fs.get(basePath) as Folder; - if (basePath !== fileOrFolder.path) { - Debug.assert(!!baseFolder); - filterMutate(baseFolder.entries, entry => entry !== fileOrFolder); - } - this.fs.delete(fileOrFolder.path); - - if (isFile(fileOrFolder)) { - this.invokeFileWatcher(fileOrFolder.fullPath, FileWatcherEventKind.Deleted); - } - else { - Debug.assert(fileOrFolder.entries.length === 0); - const relativePath = this.getRelativePathToDirectory(fileOrFolder.fullPath, fileOrFolder.fullPath); - // Invoke directory and recursive directory watcher for the folder - // Here we arent invoking recursive directory watchers for the base folders - // since that is something we would want to do for both file as well as folder we are deleting - invokeWatcherCallbacks(this.watchedDirectories.get(fileOrFolder.path), cb => cb(relativePath)); - invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrFolder.path), cb => cb(relativePath)); - } - - if (basePath !== fileOrFolder.path) { - if (baseFolder.entries.length === 0 && isRemovableLeafFolder(baseFolder)) { - this.removeFileOrFolder(baseFolder, isRemovableLeafFolder); - } - else { - this.invokeRecursiveDirectoryWatcher(baseFolder.fullPath, fileOrFolder.fullPath); - } - } - } - - private invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind) { - const callbacks = this.watchedFiles.get(this.toPath(fileFullPath)); - const fileName = getBaseFileName(fileFullPath); - invokeWatcherCallbacks(callbacks, cb => cb(fileName, eventKind)); - } - - private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) { - return getRelativePathToDirectoryOrUrl(directoryFullPath, fileFullPath, this.currentDirectory, this.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false); - } - - /** - * This will call the directory watcher for the folderFullPath and recursive directory watchers for this and base folders - */ - private invokeDirectoryWatcher(folderFullPath: string, fileName: string) { - const relativePath = this.getRelativePathToDirectory(folderFullPath, fileName); - invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => cb(relativePath)); - this.invokeRecursiveDirectoryWatcher(folderFullPath, fileName); - } - - /** - * This will call the recursive directory watcher for this directory as well as all the base directories - */ - private invokeRecursiveDirectoryWatcher(fullPath: string, fileName: string) { - const relativePath = this.getRelativePathToDirectory(fullPath, fileName); - invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), cb => cb(relativePath)); - const basePath = getDirectoryPath(fullPath); - if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) { - this.invokeRecursiveDirectoryWatcher(basePath, fileName); - } - } - - private toFile(fileOrFolder: FileOrFolder): File { - const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory); - return { - path: this.toPath(fullPath), - content: fileOrFolder.content, - fullPath, - fileSize: fileOrFolder.fileSize - }; - } - - private toFolder(path: string): Folder { - const fullPath = getNormalizedAbsolutePath(path, this.currentDirectory); - return { - path: this.toPath(fullPath), - entries: [], - fullPath - }; - } - - fileExists(s: string) { - const path = this.toFullPath(s); - return isFile(this.fs.get(path)); - } - - getFileSize(s: string) { - const path = this.toFullPath(s); - const entry = this.fs.get(path); - if (isFile(entry)) { - return entry.fileSize ? entry.fileSize : entry.content.length; - } - return undefined; - } - - directoryExists(s: string) { - const path = this.toFullPath(s); - return isFolder(this.fs.get(path)); - } - - getDirectories(s: string) { - const path = this.toFullPath(s); - const folder = this.fs.get(path); - if (isFolder(folder)) { - return mapDefined(folder.entries, entry => isFolder(entry) ? getBaseFileName(entry.fullPath) : undefined); - } - Debug.fail(folder ? "getDirectories called on file" : "getDirectories called on missing folder"); - return []; - } - - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { - return ts.matchFiles(this.toFullPath(path), extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => { - const directories: string[] = []; - const files: string[] = []; - const dirEntry = this.fs.get(this.toPath(dir)); - if (isFolder(dirEntry)) { - dirEntry.entries.forEach((entry) => { - if (isFolder(entry)) { - directories.push(getBaseFileName(entry.fullPath)); - } - else if (isFile(entry)) { - files.push(getBaseFileName(entry.fullPath)); - } - else { - Debug.fail("Unknown entry"); - } - }); - } - return { directories, files }; - }); - } - - watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { - const path = this.toFullPath(directoryName); - const map = recursive ? this.watchedDirectoriesRecursive : this.watchedDirectories; - map.add(path, callback); - return { - referenceCount: 0, - directoryName, - close: () => map.remove(path, callback) - }; - } - - createHash(s: string): string { - return Harness.LanguageService.mockHash(s); - } - - watchFile(fileName: string, callback: FileWatcherCallback) { - const path = this.toFullPath(fileName); - this.watchedFiles.add(path, callback); - return { close: () => this.watchedFiles.remove(path, callback) }; - } - - // TOOD: record and invoke callbacks to simulate timer events - setTimeout(callback: TimeOutCallback, _time: number, ...args: any[]) { - return this.timeoutCallbacks.register(callback, args); - } - - clearTimeout(timeoutId: any): void { - this.timeoutCallbacks.unregister(timeoutId); - } - - checkTimeoutQueueLengthAndRun(expected: number) { - this.checkTimeoutQueueLength(expected); - this.runQueuedTimeoutCallbacks(); - } - - checkTimeoutQueueLength(expected: number) { - const callbacksCount = this.timeoutCallbacks.count(); - assert.equal(callbacksCount, expected, `expected ${expected} timeout callbacks queued but found ${callbacksCount}.`); - } - - runQueuedTimeoutCallbacks() { - this.timeoutCallbacks.invoke(); - } - - runQueuedImmediateCallbacks() { - this.immediateCallbacks.invoke(); - } - - setImmediate(callback: TimeOutCallback, _time: number, ...args: any[]) { - return this.immediateCallbacks.register(callback, args); - } - - clearImmediate(timeoutId: any): void { - this.immediateCallbacks.unregister(timeoutId); - } - - createDirectory(directoryName: string): void { - const folder = this.toFolder(directoryName); - - // base folder has to be present - const base = getDirectoryPath(folder.fullPath); - const baseFolder = this.fs.get(base) as Folder; - Debug.assert(isFolder(baseFolder)); - - Debug.assert(!this.fs.get(folder.path)); - this.addFileOrFolderInFolder(baseFolder, folder); - } - - writeFile(path: string, content: string): void { - const file = this.toFile({ path, content }); - - // base folder has to be present - const base = getDirectoryPath(file.fullPath); - const folder = this.fs.get(base) as Folder; - Debug.assert(isFolder(folder)); - - this.addFileOrFolderInFolder(folder, file); - } - - write(message: string) { - this.output.push(message); - } - - getOutput(): ReadonlyArray { - return this.output; - } - - clearOutput() { - clear(this.output); - } - - readonly readFile = (s: string) => (this.fs.get(this.toFullPath(s))).content; - readonly resolvePath = (s: string) => s; - readonly getExecutingFilePath = () => this.executingFilePath; - readonly getCurrentDirectory = () => this.currentDirectory; - readonly exit = notImplemented; - readonly getEnvironmentVariable = notImplemented; - } - /** * Test server cancellation token used to mock host token cancellation requests. * The cancelAfterRequest constructor param specifies how many isCancellationRequested() calls @@ -845,7 +357,8 @@ namespace ts.projectSystem { checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]); const configFileLocations = ["/a/b/c/", "/a/b/", "/a/", "/"]; const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]); - checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path)); + const moduleLookupLocations = ["/a/b/c/module.ts", "/a/b/c/module.tsx"]; + checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path, ...moduleLookupLocations)); }); it("can handle tsconfig file name with difference casing", () => { @@ -2488,7 +2001,7 @@ namespace ts.projectSystem { const session = createSession(host, { canUseEvents: true, eventHandler: e => { - if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) { + if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectChangedEvent || e.eventName === server.ProjectInfoTelemetryEvent) { return; } assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); @@ -4483,4 +3996,1008 @@ namespace ts.projectSystem { } }); }); + + describe("CachingFileSystemInformation", () => { + type CalledMaps = { + fileExists: MultiMap; + directoryExists: MultiMap; + getDirectories: MultiMap; + readFile: MultiMap; + readDirectory: MultiMap<[ReadonlyArray, ReadonlyArray, ReadonlyArray, number]>; + }; + + function createCallsTrackingHost(host: TestServerHost) { + const keys: Array = ["fileExists", "directoryExists", "getDirectories", "readFile", "readDirectory"]; + const calledMaps = getCallsTrackingMap(); + return { + verifyNoCall, + verifyCalledOnEachEntryOnce, + verifyCalledOnEachEntry, + verifyNoHostCalls, + verifyNoHostCallsExceptFileExistsOnce, + clear + }; + + function getCallsTrackingMap() { + const calledMaps: { [s: string]: Map } = {}; + for (let i = 0; i < keys.length - 1; i++) { + setCallsTrackingWithSingleArgFn(keys[i]); + } + setCallsTrackingWithFiveArgFn(keys[keys.length - 1]); + return calledMaps as CalledMaps; + + function setCallsTrackingWithSingleArgFn(prop: keyof CalledMaps) { + const calledMap = createMultiMap(); + const cb = (host)[prop].bind(host); + (host)[prop] = (f: string) => { + calledMap.add(f, /*value*/ true); + return cb(f); + }; + calledMaps[prop] = calledMap; + } + + function setCallsTrackingWithFiveArgFn(prop: keyof CalledMaps) { + const calledMap = createMultiMap<[U, V, W, X]>(); + const cb = (host)[prop].bind(host); + (host)[prop] = (f: string, arg1?: U, arg2?: V, arg3?: W, arg4?: X) => { + calledMap.add(f, [arg1, arg2, arg3, arg4]); + return cb(f, arg1, arg2, arg3, arg4); + }; + calledMaps[prop] = calledMap; + } + } + + function verifyNoCall(callback: keyof CalledMaps) { + const calledMap = calledMaps[callback]; + assert.equal(calledMap.size, 0, `${callback} shouldnt be called: ${arrayFrom(calledMap.keys())}`); + } + + function verifyCalledOnEachEntry(callback: keyof CalledMaps, expectedKeys: Map) { + const calledMap = calledMaps[callback]; + assert.equal(calledMap.size, expectedKeys.size, `${callback}: incorrect size of map: Actual keys: ${arrayFrom(calledMap.keys())} Expected: ${arrayFrom(expectedKeys.keys())}`); + expectedKeys.forEach((called, name) => { + assert.isTrue(calledMap.has(name), `${callback} is expected to contain ${name}, actual keys: ${arrayFrom(calledMap.keys())}`); + assert.equal(calledMap.get(name).length, called, `${callback} is expected to be called ${called} times with ${name}. Actual entry: ${calledMap.get(name)}`); + }); + } + + function verifyCalledOnEachEntryOnce(callback: keyof CalledMaps, expectedKeys: string[]) { + return verifyCalledOnEachEntry(callback, zipToMap(expectedKeys, expectedKeys.map(() => 1))); + } + + function verifyNoHostCalls() { + for (const key of keys) { + verifyNoCall(key); + } + } + + function verifyNoHostCallsExceptFileExistsOnce(expectedKeys: string[]) { + verifyCalledOnEachEntryOnce("fileExists", expectedKeys); + verifyNoCall("directoryExists"); + verifyNoCall("getDirectories"); + verifyNoCall("readFile"); + verifyNoCall("readDirectory"); + } + + function clear() { + for (const key of keys) { + calledMaps[key].clear(); + } + } + } + + it("when calling goto definition of module", () => { + const clientFile: FileOrFolder = { + path: "/a/b/controllers/vessels/client.ts", + content: ` + import { Vessel } from '~/models/vessel'; + const v = new Vessel(); + ` + }; + const anotherModuleFile: FileOrFolder = { + path: "/a/b/utils/db.ts", + content: "export class Bookshelf { }" + }; + const moduleFile: FileOrFolder = { + path: "/a/b/models/vessel.ts", + content: ` + import { Bookshelf } from '~/utils/db'; + export class Vessel extends Bookshelf {} + ` + }; + const tsconfigFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: JSON.stringify({ + compilerOptions: { + target: "es6", + module: "es6", + baseUrl: "./", // all paths are relative to the baseUrl + paths: { + "~/*": ["*"] // resolve any `~/foo/bar` to `/foo/bar` + } + }, + exclude: [ + "api", + "build", + "node_modules", + "public", + "seeds", + "sql_updates", + "tests.build" + ] + }) + }; + const projectFiles = [clientFile, anotherModuleFile, moduleFile, tsconfigFile]; + const host = createServerHost(projectFiles); + const session = createSession(host); + const projectService = session.getProjectService(); + const { configFileName } = projectService.openClientFile(clientFile.path); + + assert.isDefined(configFileName, `should find config`); + checkNumberOfConfiguredProjects(projectService, 1); + + const project = projectService.configuredProjects.get(tsconfigFile.path); + checkProjectActualFiles(project, map(projectFiles, f => f.path)); + + const callsTrackingHost = createCallsTrackingHost(host); + + // Get definitions shouldnt make host requests + const getDefinitionRequest = makeSessionRequest(protocol.CommandTypes.Definition, { + file: clientFile.path, + position: clientFile.content.indexOf("/vessel") + 1, + line: undefined, + offset: undefined + }); + const { response } = session.executeCommand(getDefinitionRequest); + assert.equal(response[0].file, moduleFile.path, "Should go to definition of vessel: response: " + JSON.stringify(response)); + callsTrackingHost.verifyNoHostCalls(); + + // Open the file should call only file exists on module directory and use cached value for parental directory + const { configFileName: config2 } = projectService.openClientFile(moduleFile.path); + assert.equal(config2, configFileName); + callsTrackingHost.verifyNoHostCallsExceptFileExistsOnce(["/a/b/models/tsconfig.json", "/a/b/models/jsconfig.json"]); + + checkNumberOfConfiguredProjects(projectService, 1); + assert.strictEqual(projectService.configuredProjects.get(tsconfigFile.path), project); + }); + + describe("WatchDirectories for config file with", () => { + function verifyWatchDirectoriesCaseSensitivity(useCaseSensitiveFileNames: boolean) { + const frontendDir = "/Users/someuser/work/applications/frontend"; + const canonicalFrontendDir = useCaseSensitiveFileNames ? frontendDir : frontendDir.toLowerCase(); + const file1: FileOrFolder = { + path: `${frontendDir}/src/app/utils/Analytic.ts`, + content: "export class SomeClass { };" + }; + const file2: FileOrFolder = { + path: `${frontendDir}/src/app/redux/configureStore.ts`, + content: "export class configureStore { }" + }; + const file3: FileOrFolder = { + path: `${frontendDir}/src/app/utils/Cookie.ts`, + content: "export class Cookie { }" + }; + const es2016LibFile: FileOrFolder = { + path: "/a/lib/lib.es2016.full.d.ts", + content: libFile.content + }; + const tsconfigFile: FileOrFolder = { + path: `${frontendDir}/tsconfig.json`, + content: JSON.stringify({ + "compilerOptions": { + "strict": true, + "strictNullChecks": true, + "target": "es2016", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "noEmitOnError": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "types": [ + "node", + "jest" + ], + "noUnusedLocals": true, + "outDir": "./compiled", + "typeRoots": [ + "types", + "node_modules/@types" + ], + "baseUrl": ".", + "paths": { + "*": [ + "types/*" + ] + } + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "compiled" + ] + }) + }; + const projectFiles = [file1, file2, es2016LibFile, tsconfigFile]; + const host = createServerHost(projectFiles, { useCaseSensitiveFileNames }); + const projectService = createProjectService(host); + const canonicalConfigPath = useCaseSensitiveFileNames ? tsconfigFile.path : tsconfigFile.path.toLowerCase(); + const { configFileName } = projectService.openClientFile(file1.path); + assert.equal(configFileName, tsconfigFile.path, `should find config`); + checkNumberOfConfiguredProjects(projectService, 1); + + const project = projectService.configuredProjects.get(canonicalConfigPath); + verifyProjectAndWatchedDirectories(); + + const callsTrackingHost = createCallsTrackingHost(host); + + // Create file cookie.ts + projectFiles.push(file3); + host.reloadFS(projectFiles); + host.runQueuedTimeoutCallbacks(); + + const canonicalFile3Path = useCaseSensitiveFileNames ? file3.path : file3.path.toLocaleLowerCase(); + callsTrackingHost.verifyCalledOnEachEntryOnce("fileExists", [canonicalFile3Path]); + + // Called for type root resolution + const directoryExistsCalled = createMap(); + for (let dir = frontendDir; dir !== "/"; dir = getDirectoryPath(dir)) { + directoryExistsCalled.set(`${dir}/node_modules`, 2); + } + directoryExistsCalled.set(`/node_modules`, 2); + directoryExistsCalled.set(`${frontendDir}/types`, 2); + directoryExistsCalled.set(`${frontendDir}/node_modules/@types`, 2); + directoryExistsCalled.set(canonicalFile3Path, 1); + callsTrackingHost.verifyCalledOnEachEntry("directoryExists", directoryExistsCalled); + + callsTrackingHost.verifyNoCall("getDirectories"); + callsTrackingHost.verifyCalledOnEachEntryOnce("readFile", [file3.path]); + callsTrackingHost.verifyNoCall("readDirectory"); + + checkNumberOfConfiguredProjects(projectService, 1); + assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project); + verifyProjectAndWatchedDirectories(); + + callsTrackingHost.clear(); + + const { configFileName: configFile2 } = projectService.openClientFile(file3.path); + assert.equal(configFile2, configFileName); + + checkNumberOfConfiguredProjects(projectService, 1); + assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project); + verifyProjectAndWatchedDirectories(); + callsTrackingHost.verifyNoHostCalls(); + + function verifyProjectAndWatchedDirectories() { + checkProjectActualFiles(project, map(projectFiles, f => f.path)); + checkWatchedDirectories(host, [`${canonicalFrontendDir}/src`], /*recursive*/ true); + checkWatchedDirectories(host, [`${canonicalFrontendDir}/types`, `${canonicalFrontendDir}/node_modules/@types`], /*recursive*/ false); + } + } + + it("case insensitive file system", () => { + verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ false); + }); + + it("case sensitive file system", () => { + verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ true); + }); + }); + + describe("Verify npm install in directory with tsconfig file works when", () => { + function verifyNpmInstall(timeoutDuringPartialInstallation: boolean) { + const app: FileOrFolder = { + path: "/a/b/app.ts", + content: "import _ from 'lodash';" + }; + const tsconfigJson: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: '{ "compilerOptions": { } }' + }; + const packageJson: FileOrFolder = { + path: "/a/b/package.json", + content: ` +{ + "name": "test", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "lodash", + "rxjs" + }, + "devDependencies": { + "@types/lodash", + "typescript" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} +` + }; + const appFolder = getDirectoryPath(app.path); + const projectFiles = [app, libFile, tsconfigJson]; + const otherFiles = [packageJson]; + const host = createServerHost(projectFiles.concat(otherFiles)); + const projectService = createProjectService(host); + const { configFileName } = projectService.openClientFile(app.path); + assert.equal(configFileName, tsconfigJson.path, `should find config`); + const watchedModuleLocations = getNodeModulesWatchedDirectories(appFolder, "lodash"); + verifyProject(); + + let timeoutAfterReloadFs = timeoutDuringPartialInstallation; + + // Simulate npm install + const filesAndFoldersToAdd: FileOrFolder[] = [ + { "path": "/a/b/node_modules" }, + { "path": "/a/b/node_modules/.staging/@types" }, + { "path": "/a/b/node_modules/.staging/lodash-b0733faa" }, + { "path": "/a/b/node_modules/.staging/@types/lodash-e56c4fe7" }, + { "path": "/a/b/node_modules/.staging/symbol-observable-24bcbbff" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61" }, + { "path": "/a/b/node_modules/.staging/typescript-8493ea5d" }, + { "path": "/a/b/node_modules/.staging/symbol-observable-24bcbbff/package.json", "content": "{\n \"name\": \"symbol-observable\",\n \"version\": \"1.0.4\",\n \"description\": \"Symbol.observable ponyfill\",\n \"license\": \"MIT\",\n \"repository\": \"blesh/symbol-observable\",\n \"author\": {\n \"name\": \"Ben Lesh\",\n \"email\": \"ben@benlesh.com\"\n },\n \"engines\": {\n \"node\": \">=0.10.0\"\n },\n \"scripts\": {\n \"test\": \"npm run build && mocha && tsc ./ts-test/test.ts && node ./ts-test/test.js && check-es3-syntax -p lib/ --kill\",\n \"build\": \"babel es --out-dir lib\",\n \"prepublish\": \"npm test\"\n },\n \"files\": [\n \"" }, + { "path": "/a/b/node_modules/.staging/lodash-b0733faa/package.json", "content": "{\n \"name\": \"lodash\",\n \"version\": \"4.17.4\",\n \"description\": \"Lodash modular utilities.\",\n \"keywords\": \"modules, stdlib, util\",\n \"homepage\": \"https://lodash.com/\",\n \"repository\": \"lodash/lodash\",\n \"icon\": \"https://lodash.com/icon.svg\",\n \"license\": \"MIT\",\n \"main\": \"lodash.js\",\n \"author\": \"John-David Dalton (http://allyoucanleet.com/)\",\n \"contributors\": [\n \"John-David Dalton (http://allyoucanleet.com/)\",\n \"Mathias Bynens \",\n \"contributors\": [\n {\n \"name\": \"Ben Lesh\",\n \"email\": \"ben@benlesh.com\"\n },\n {\n \"name\": \"Paul Taylor\",\n \"email\": \"paul.e.taylor@me.com\"\n },\n {\n \"name\": \"Jeff Cross\",\n \"email\": \"crossj@google.com\"\n },\n {\n \"name\": \"Matthew Podwysocki\",\n \"email\": \"matthewp@microsoft.com\"\n },\n {\n \"name\": \"OJ Kwon\",\n \"email\": \"kwon.ohjoong@gmail.com\"\n },\n {\n \"name\": \"Andre Staltz\",\n \"email\": \"andre@staltz.com\"\n }\n ],\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/ReactiveX/RxJS/issues\"\n },\n \"homepage\": \"https://github.com/ReactiveX/RxJS\",\n \"devDependencies\": {\n \"babel-polyfill\": \"^6.23.0\",\n \"benchmark\": \"^2.1.0\",\n \"benchpress\": \"2.0.0-beta.1\",\n \"chai\": \"^3.5.0\",\n \"color\": \"^0.11.1\",\n \"colors\": \"1.1.2\",\n \"commitizen\": \"^2.8.6\",\n \"coveralls\": \"^2.11.13\",\n \"cz-conventional-changelog\": \"^1.2.0\",\n \"danger\": \"^1.1.0\",\n \"doctoc\": \"^1.0.0\",\n \"escape-string-regexp\": \"^1.0.5 \",\n \"esdoc\": \"^0.4.7\",\n \"eslint\": \"^3.8.0\",\n \"fs-extra\": \"^2.1.2\",\n \"get-folder-size\": \"^1.0.0\",\n \"glob\": \"^7.0.3\",\n \"gm\": \"^1.22.0\",\n \"google-closure-compiler-js\": \"^20170218.0.0\",\n \"gzip-size\": \"^3.0.0\",\n \"http-server\": \"^0.9.0\",\n \"husky\": \"^0.13.3\",\n \"lint-staged\": \"3.2.5\",\n \"lodash\": \"^4.15.0\",\n \"madge\": \"^1.4.3\",\n \"markdown-doctest\": \"^0.9.1\",\n \"minimist\": \"^1.2.0\",\n \"mkdirp\": \"^0.5.1\",\n \"mocha\": \"^3.0.2\",\n \"mocha-in-sauce\": \"0.0.1\",\n \"npm-run-all\": \"^4.0.2\",\n \"npm-scripts-info\": \"^0.3.4\",\n \"nyc\": \"^10.2.0\",\n \"opn-cli\": \"^3.1.0\",\n \"platform\": \"^1.3.1\",\n \"promise\": \"^7.1.1\",\n \"protractor\": \"^3.1.1\",\n \"rollup\": \"0.36.3\",\n \"rollup-plugin-inject\": \"^2.0.0\",\n \"rollup-plugin-node-resolve\": \"^2.0.0\",\n \"rx\": \"latest\",\n \"rxjs\": \"latest\",\n \"shx\": \"^0.2.2\",\n \"sinon\": \"^2.1.0\",\n \"sinon-chai\": \"^2.9.0\",\n \"source-map-support\": \"^0.4.0\",\n \"tslib\": \"^1.5.0\",\n \"tslint\": \"^4.4.2\",\n \"typescript\": \"~2.0.6\",\n \"typings\": \"^2.0.0\",\n \"validate-commit-msg\": \"^2.14.0\",\n \"watch\": \"^1.0.1\",\n \"webpack\": \"^1.13.1\",\n \"xmlhttprequest\": \"1.8.0\"\n },\n \"engines\": {\n \"npm\": \">=2.0.0\"\n },\n \"typings\": \"Rx.d.ts\",\n \"dependencies\": {\n \"symbol-observable\": \"^1.0.1\"\n }\n}" }, + { "path": "/a/b/node_modules/.staging/typescript-8493ea5d/package.json", "content": "{\n \"name\": \"typescript\",\n \"author\": \"Microsoft Corp.\",\n \"homepage\": \"http://typescriptlang.org/\",\n \"version\": \"2.4.2\",\n \"license\": \"Apache-2.0\",\n \"description\": \"TypeScript is a language for application scale JavaScript development\",\n \"keywords\": [\n \"TypeScript\",\n \"Microsoft\",\n \"compiler\",\n \"language\",\n \"javascript\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/Microsoft/TypeScript/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Microsoft/TypeScript.git\"\n },\n \"main\": \"./lib/typescript.js\",\n \"typings\": \"./lib/typescript.d.ts\",\n \"bin\": {\n \"tsc\": \"./bin/tsc\",\n \"tsserver\": \"./bin/tsserver\"\n },\n \"engines\": {\n \"node\": \">=4.2.0\"\n },\n \"devDependencies\": {\n \"@types/browserify\": \"latest\",\n \"@types/chai\": \"latest\",\n \"@types/convert-source-map\": \"latest\",\n \"@types/del\": \"latest\",\n \"@types/glob\": \"latest\",\n \"@types/gulp\": \"latest\",\n \"@types/gulp-concat\": \"latest\",\n \"@types/gulp-help\": \"latest\",\n \"@types/gulp-newer\": \"latest\",\n \"@types/gulp-sourcemaps\": \"latest\",\n \"@types/merge2\": \"latest\",\n \"@types/minimatch\": \"latest\",\n \"@types/minimist\": \"latest\",\n \"@types/mkdirp\": \"latest\",\n \"@types/mocha\": \"latest\",\n \"@types/node\": \"latest\",\n \"@types/q\": \"latest\",\n \"@types/run-sequence\": \"latest\",\n \"@types/through2\": \"latest\",\n \"browserify\": \"latest\",\n \"chai\": \"latest\",\n \"convert-source-map\": \"latest\",\n \"del\": \"latest\",\n \"gulp\": \"latest\",\n \"gulp-clone\": \"latest\",\n \"gulp-concat\": \"latest\",\n \"gulp-help\": \"latest\",\n \"gulp-insert\": \"latest\",\n \"gulp-newer\": \"latest\",\n \"gulp-sourcemaps\": \"latest\",\n \"gulp-typescript\": \"latest\",\n \"into-stream\": \"latest\",\n \"istanbul\": \"latest\",\n \"jake\": \"latest\",\n \"merge2\": \"latest\",\n \"minimist\": \"latest\",\n \"mkdirp\": \"latest\",\n \"mocha\": \"latest\",\n \"mocha-fivemat-progress-reporter\": \"latest\",\n \"q\": \"latest\",\n \"run-sequence\": \"latest\",\n \"sorcery\": \"latest\",\n \"through2\": \"latest\",\n \"travis-fold\": \"latest\",\n \"ts-node\": \"latest\",\n \"tslint\": \"latest\",\n \"typescript\": \"^2.4\"\n },\n \"scripts\": {\n \"pretest\": \"jake tests\",\n \"test\": \"jake runtests-parallel\",\n \"build\": \"npm run build:compiler && npm run build:tests\",\n \"build:compiler\": \"jake local\",\n \"build:tests\": \"jake tests\",\n \"start\": \"node lib/tsc\",\n \"clean\": \"jake clean\",\n \"gulp\": \"gulp\",\n \"jake\": \"jake\",\n \"lint\": \"jake lint\",\n \"setup-hooks\": \"node scripts/link-hooks.js\"\n },\n \"browser\": {\n \"buffer\": false,\n \"fs\": false,\n \"os\": false,\n \"path\": false\n }\n}" }, + { "path": "/a/b/node_modules/.staging/symbol-observable-24bcbbff/index.js", "content": "module.exports = require('./lib/index');\n" }, + { "path": "/a/b/node_modules/.staging/symbol-observable-24bcbbff/index.d.ts", "content": "declare const observableSymbol: symbol;\nexport default observableSymbol;\n" }, + { "path": "/a/b/node_modules/.staging/symbol-observable-24bcbbff/lib" }, + { "path": "/a/b/node_modules/.staging/symbol-observable-24bcbbff/lib/index.js", "content": "'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _ponyfill = require('./ponyfill');\n\nvar _ponyfill2 = _interopRequireDefault(_ponyfill);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nvar root; /* global window */\n\n\nif (typeof self !== 'undefined') {\n root = self;\n} else if (typeof window !== 'undefined') {\n root = window;\n} else if (typeof global !== 'undefined') {\n root = global;\n} else if (typeof module !== 'undefined') {\n root = module;\n} else {\n root = Function('return this')();\n}\n\nvar result = (0, _ponyfill2['default'])(root);\nexports['default'] = result;" }, + ]; + verifyAfterPartialOrCompleteNpmInstall(2); + + filesAndFoldersToAdd.push( + { "path": "/a/b/node_modules/.staging/typescript-8493ea5d/lib" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/add/operator" }, + { "path": "/a/b/node_modules/.staging/@types/lodash-e56c4fe7/package.json", "content": "{\n \"name\": \"@types/lodash\",\n \"version\": \"4.14.74\",\n \"description\": \"TypeScript definitions for Lo-Dash\",\n \"license\": \"MIT\",\n \"contributors\": [\n {\n \"name\": \"Brian Zengel\",\n \"url\": \"https://github.com/bczengel\"\n },\n {\n \"name\": \"Ilya Mochalov\",\n \"url\": \"https://github.com/chrootsu\"\n },\n {\n \"name\": \"Stepan Mikhaylyuk\",\n \"url\": \"https://github.com/stepancar\"\n },\n {\n \"name\": \"Eric L Anderson\",\n \"url\": \"https://github.com/ericanderson\"\n },\n {\n \"name\": \"AJ Richardson\",\n \"url\": \"https://github.com/aj-r\"\n },\n {\n \"name\": \"Junyoung Clare Jang\",\n \"url\": \"https://github.com/ailrun\"\n }\n ],\n \"main\": \"\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://www.github.com/DefinitelyTyped/DefinitelyTyped.git\"\n },\n \"scripts\": {},\n \"dependencies\": {},\n \"typesPublisherContentHash\": \"12af578ffaf8d86d2df37e591857906a86b983fa9258414326544a0fe6af0de8\",\n \"typeScriptVersion\": \"2.2\"\n}" }, + { "path": "/a/b/node_modules/.staging/lodash-b0733faa/index.js", "content": "module.exports = require('./lodash');" }, + { "path": "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594" } + ); + // Since we didnt add any supported extension file, there wont be any timeout scheduled + verifyAfterPartialOrCompleteNpmInstall(0); + + // Remove file "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594" + filesAndFoldersToAdd.length--; + verifyAfterPartialOrCompleteNpmInstall(0); + + filesAndFoldersToAdd.push( + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/bundles" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/operator" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/src/add/observable/dom" }, + { "path": "/a/b/node_modules/.staging/@types/lodash-e56c4fe7/index.d.ts", "content": "// Type definitions for Lo-Dash 4.14\n// Project: http://lodash.com/\n// Definitions by: Brian Zengel ,\n// Ilya Mochalov ,\n// Stepan Mikhaylyuk ,\n// Eric L Anderson ,\n// AJ Richardson ,\n// Junyoung Clare Jang \n// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped\n// TypeScript Version: 2.2\n\n/**\n### 4.0.0 Changelog (https://github.com/lodash/lodash/wiki/Changelog)\n\n#### TODO:\nremoved:\n- [x] Removed _.support\n- [x] Removed _.findWhere in favor of _.find with iteratee shorthand\n- [x] Removed _.where in favor of _.filter with iteratee shorthand\n- [x] Removed _.pluck in favor of _.map with iteratee shorthand\n\nrenamed:\n- [x] Renamed _.first to _.head\n- [x] Renamed _.indexBy to _.keyBy\n- [x] Renamed _.invoke to _.invokeMap\n- [x] Renamed _.overArgs to _.overArgs\n- [x] Renamed _.padLeft & _.padRight to _.padStart & _.padEnd\n- [x] Renamed _.pairs to _.toPairs\n- [x] Renamed _.rest to _.tail\n- [x] Renamed _.restParam to _.rest\n- [x] Renamed _.sortByOrder to _.orderBy\n- [x] Renamed _.trimLeft & _.trimRight to _.trimStart & _.trimEnd\n- [x] Renamed _.trunc to _.truncate\n\nsplit:\n- [x] Split _.indexOf & _.lastIndexOf into _.sortedIndexOf & _.sortedLastIndexOf\n- [x] Split _.max & _.min into _.maxBy & _.minBy\n- [x] Split _.omit & _.pick into _.omitBy & _.pickBy\n- [x] Split _.sample into _.sampleSize\n- [x] Split _.sortedIndex into _.sortedIndexBy\n- [x] Split _.sortedLastIndex into _.sortedLastIndexBy\n- [x] Split _.uniq into _.sortedUniq, _.sortedUniqBy, & _.uniqBy\n\nchanges:\n- [x] Absorbed _.sortByAll into _.sortBy\n- [x] Changed the category of _.at to “Object”\n- [x] Changed the category of _.bindAll to “Utility”\n- [x] Made _.capitalize uppercase the first character & lowercase the rest\n- [x] Made _.functions return only own method names\n\nadded 23 array methods:\n- [x] _.concat\n- [x] _.differenceBy\n- [x] _.differenceWith\n- [x] _.flatMap\n- [x] _.fromPairs\n- [x] _.intersectionBy\n- [x] _.intersectionWith\n- [x] _.join\n- [x] _.pullAll\n- [x] _.pullAllBy\n- [x] _.reverse\n- [x] _.sortedIndexBy\n- [x] _.sortedIndexOf\n- [x] _.sortedLastIndexBy\n- [x] _.sortedLastIndexOf\n- [x] _.sortedUniq\n- [x] _.sortedUniqBy\n- [x] _.unionBy\n- [x] _.unionWith\n- [x] _.uniqBy\n- [x] _.uniqWith\n- [x] _.xorBy\n- [x] _.xorWith\n\nadded 20 lang methods:\n- [x] _.cloneDeepWith\n- [x] _.cloneWith\n- [x] _.eq\n- [x] _.isArrayLike\n- [x] _.isArrayLikeObject\n- [x] _.isEqualWith\n- [x] _.isInteger\n- [x] _.isLength\n- [x] _.isMatchWith\n- [x] _.isNil\n- [x] _.isObjectLike\n- [x] _.isSafeInteger\n- [x] _.isSymbol\n- [x] _.toInteger\n- [x] _.toLength\n- [x] _.toNumber\n- [x] _.toSafeInteger\n- [x] _.toString\n- [X] _.conforms\n- [X] _.conformsTo\n\nadded 13 object methods:\n- [x] _.assignIn\n- [x] _.assignInWith\n- [x] _.assignWith\n- [x] _.functionsIn\n- [x] _.hasIn\n- [x] _.mergeWith\n- [x] _.omitBy\n- [x] _.pickBy\n\nadded 8 string methods:\n- [x] _.lowerCase\n- [x] _.lowerFirst\n- [x] _.upperCase\n- [x] _.upperFirst\n- [x] _.toLower\n- [x] _.toUpper\n\nadded 8 utility methods:\n- [x] _.toPath\n\nadded 4 math methods:\n- [x] _.maxBy\n- [x] _.mean\n- [x] _.minBy\n- [x] _.sumBy\n\nadded 2 function methods:\n- [x] _.flip\n- [x] _.unary\n\nadded 2 number methods:\n- [x] _.clamp\n- [x] _.subtract\n\nadded collection method:\n- [x] _.sampleSize\n\nAdded 3 aliases\n\n- [x] _.first as an alias of _.head\n\nRemoved 17 aliases\n- [x] Removed aliase _.all\n- [x] Removed aliase _.any\n- [x] Removed aliase _.backflow\n- [x] Removed aliase _.callback\n- [x] Removed aliase _.collect\n- [x] Removed aliase _.compose\n- [x] Removed aliase _.contains\n- [x] Removed aliase _.detect\n- [x] Removed aliase _.foldl\n- [x] Removed aliase _.foldr\n- [x] Removed aliase _.include\n- [x] Removed aliase _.inject\n- [x] Removed aliase _.methods\n- [x] Removed aliase _.object\n- [x] Removed aliase _.run\n- [x] Removed aliase _.select\n- [x] Removed aliase _.unique\n\nOther changes\n- [x] Added support for array buffers to _.isEqual\n- [x] Added support for converting iterators to _.toArray\n- [x] Added support for deep paths to _.zipObject\n- [x] Changed UMD to export to window or self when available regardless of other exports\n- [x] Ensured debounce cancel clears args & thisArg references\n- [x] Ensured _.add, _.subtract, & _.sum don’t skip NaN values\n- [x] Ensured _.clone treats generators like functions\n- [x] Ensured _.clone produces clones with the source’s [[Prototype]]\n- [x] Ensured _.defaults assigns properties that shadow Object.prototype\n- [x] Ensured _.defaultsDeep doesn’t merge a string into an array\n- [x] Ensured _.defaultsDeep & _.merge don’t modify sources\n- [x] Ensured _.defaultsDeep works with circular references\n- [x] Ensured _.keys skips “length” on strict mode arguments objects in Safari 9\n- [x] Ensured _.merge doesn’t convert strings to arrays\n- [x] Ensured _.merge merges plain-objects onto non plain-objects\n- [x] Ensured _#plant resets iterator data of cloned sequences\n- [x] Ensured _.random swaps min & max if min is greater than max\n- [x] Ensured _.range preserves the sign of start of -0\n- [x] Ensured _.reduce & _.reduceRight use getIteratee in their array branch\n- [x] Fixed rounding issue with the precision param of _.floor\n- [x] Added flush method to debounced & throttled functions\n\n** LATER **\nMisc:\n- [ ] Made _.forEach, _.forIn, _.forOwn, & _.times implicitly end a chain sequence\n- [ ] Removed thisArg params from most methods\n- [ ] Made “By” methods provide a single param to iteratees\n- [ ] Made _.words chainable by default\n- [ ] Removed isDeep params from _.clone & _.flatten\n- [ ] Removed _.bindAll support for binding all methods when no names are provided\n- [ ] Removed func-first param signature from _.before & _.after\n- [ ] _.extend as an alias of _.assignIn\n- [ ] _.extendWith as an alias of _.assignInWith\n- [ ] Added clear method to _.memoize.Cache\n- [ ] Added support for ES6 maps, sets, & symbols to _.clone, _.isEqual, & _.toArray\n- [x] Enabled _.flow & _.flowRight to accept an array of functions\n- [ ] Ensured “Collection” methods treat functions as objects\n- [ ] Ensured _.assign, _.defaults, & _.merge coerce object values to objects\n- [ ] Ensured _.bindKey bound functions call object[key] when called with the new operator\n- [ ] Ensured _.isFunction returns true for generator functions\n- [ ] Ensured _.merge assigns typed arrays directly\n- [ ] Made _(...) an iterator & iterable\n- [ ] Made _.drop, _.take, & right forms coerce n of undefined to 0\n\nMethods:\n- [ ] _.concat\n- [ ] _.differenceBy\n- [ ] _.differenceWith\n- [ ] _.flatMap\n- [ ] _.fromPairs\n- [ ] _.intersectionBy\n- [ ] _.intersectionWith\n- [ ] _.join\n- [ ] _.pullAll\n- [ ] _.pullAllBy\n- [ ] _.reverse\n- [ ] _.sortedLastIndexOf\n- [ ] _.unionBy\n- [ ] _.unionWith\n- [ ] _.uniqWith\n- [ ] _.xorBy\n- [ ] _.xorWith\n- [ ] _.toString\n\n- [ ] _.invoke\n- [ ] _.setWith\n- [ ] _.toPairs\n- [ ] _.toPairsIn\n- [ ] _.unset\n\n- [ ] _.replace\n- [ ] _.split\n\n- [ ] _.cond\n- [ ] _.nthArg\n- [ ] _.over\n- [ ] _.overEvery\n- [ ] _.overSome\n- [ ] _.rangeRight\n\n- [ ] _.next\n*/\n\nexport = _;\nexport as namespace _;\n\ndeclare var _: _.LoDashStatic;\n\ntype PartialObject = Partial;\n\ndeclare namespace _ {\n type Many = T | T[];\n\n interface LoDashStatic {\n /**\n * Creates a lodash object which wraps the given value to enable intuitive method chaining.\n *\n * In addition to Lo-Dash methods, wrappers also have the following Array methods:\n * concat, join, pop, push, reverse, shift, slice, sort, splice, and unshift\n *\n * Chaining is supported in custom builds as long as the value method is implicitly or\n * explicitly included in the build.\n *\n * The chainable wrapper functions are:\n * after, assign, bind, bindAll, bindKey, chain, chunk, compact, compose, concat, countBy,\n * createCallback, curry, debounce, defaults, defer, delay, difference, filter, flatten,\n * forEach, forEachRight, forIn, forInRight, forOwn, forOwnRight, functions, groupBy,\n * keyBy, initial, intersection, invert, invoke, keys, map, max, memoize, merge, min,\n * object, omit, once, pairs, partial, partialRight, pick, pluck, pull, push, range, reject,\n * remove, rest, reverse, sample, shuffle, slice, sort, sortBy, splice, tap, throttle, times,\n * toArray, transform, union, uniq, unset, unshift, unzip, values, where, without, wrap, and zip\n *\n * The non-chainable wrapper functions are:\n * clone, cloneDeep, contains, escape, every, find, findIndex, findKey, findLast,\n * findLastIndex, findLastKey, has, identity, indexOf, isArguments, isArray, isBoolean,\n * isDate, isElement, isEmpty, isEqual, isFinite, isFunction, isNaN, isNull, isNumber,\n * isObject, isPlainObject, isRegExp, isString, isUndefined, join, lastIndexOf, mixin,\n * noConflict, parseInt, pop, random, reduce, reduceRight, result, shift, size, some,\n * sortedIndex, runInContext, template, unescape, uniqueId, and value\n *\n * The wrapper functions first and last return wrapped values when n is provided, otherwise\n * they return unwrapped values.\n *\n * Explicit chaining can be enabled by using the _.chain method.\n **/\n (value: number): LoDashImplicitWrapper;\n (value: string): LoDashImplicitStringWrapper;\n (value: boolean): LoDashImplicitWrapper;\n (value: null | undefined): LoDashImplicitWrapper;\n (value: number[]): LoDashImplicitNumberArrayWrapper;\n (value: T[]): LoDashImplicitArrayWrapper;\n (value: T[] | null | undefined): LoDashImplicitNillableArrayWrapper;\n (value: T): LoDashImplicitObjectWrapper;\n (value: T | null | undefined): LoDashImplicitNillableObjectWrapper;\n (value: any): LoDashImplicitWrapper;\n\n /**\n * The semantic version number.\n **/\n VERSION: string;\n\n /**\n * By default, the template delimiters used by Lo-Dash are similar to those in embedded Ruby\n * (ERB). Change the following template settings to use alternative delimiters.\n **/\n templateSettings: TemplateSettings;\n }\n\n /**\n * By default, the template delimiters used by Lo-Dash are similar to those in embedded Ruby\n * (ERB). Change the following template settings to use alternative delimiters.\n **/\n interface TemplateSettings {\n /**\n * The \"escape\" delimiter.\n **/\n escape?: RegExp;\n\n /**\n * The \"evaluate\" delimiter.\n **/\n evaluate?: RegExp;\n\n /**\n * An object to import into the template as local variables.\n **/\n imports?: Dictionary;\n\n /**\n * The \"interpolate\" delimiter.\n **/\n interpolate?: RegExp;\n\n /**\n * Used to reference the data object in the template text.\n **/\n variable?: string;\n }\n\n /**\n * Creates a cache object to store key/value pairs.\n */\n interface MapCache {\n /**\n * Removes `key` and its value from the cache.\n * @param key The key of the value to remove.\n * @return Returns `true` if the entry was removed successfully, else `false`.\n */\n delete(key: string): boolean;\n\n /**\n * Gets the cached value for `key`.\n * @param key The key of the value to get.\n * @return Returns the cached value.\n */\n get(key: string): any;\n\n /**\n * Checks if a cached value for `key` exists.\n * @param key The key of the entry to check.\n * @return Returns `true` if an entry for `key` exists, else `false`.\n */\n has(key: string): boolean;\n\n /**\n * Sets `value` to `key` of the cache.\n * @param key The key of the value to cache.\n * @param value The value to cache.\n * @return Returns the cache object.\n */\n set(key: string, value: any): _.Dictionary;\n\n /**\n * Removes all key-value entries from the map.\n */\n clear(): void;\n }\n interface MapCacheConstructor {\n new (): MapCache;\n }\n\n interface LoDashWrapperBase { }\n\n interface LoDashImplicitWrapperBase extends LoDashWrapperBase { }\n\n interface LoDashExplicitWrapperBase extends LoDashWrapperBase { }\n\n interface LoDashImplicitWrapper extends LoDashImplicitWrapperBase> { }\n\n interface LoDashExplicitWrapper extends LoDashExplicitWrapperBase> { }\n\n interface LoDashImplicitStringWrapper extends LoDashImplicitWrapper { }\n\n interface LoDashExplicitStringWrapper extends LoDashExplicitWrapper { }\n\n interface LoDashImplicitObjectWrapperBase extends LoDashImplicitWrapperBase { }\n\n interface LoDashImplicitObjectWrapper extends LoDashImplicitObjectWrapperBase> { }\n\n interface LoDashImplicitNillableObjectWrapper extends LoDashImplicitObjectWrapperBase> { }\n\n interface LoDashExplicitObjectWrapperBase extends LoDashExplicitWrapperBase { }\n\n interface LoDashExplicitObjectWrapper extends LoDashExplicitObjectWrapperBase> { }\n\n interface LoDashExplicitNillableObjectWrapper extends LoDashExplicitObjectWrapperBase> { }\n\n interface LoDashImplicitArrayWrapperBase extends LoDashImplicitWrapperBase {\n pop(): T | undefined;\n push(...items: T[]): TWrapper;\n shift(): T | undefined;\n sort(compareFn?: (a: T, b: T) => number): TWrapper;\n splice(start: number): TWrapper;\n splice(start: number, deleteCount: number, ...items: T[]): TWrapper;\n unshift(...items: T[]): TWrapper;\n }\n\n interface LoDashImplicitArrayWrapper extends LoDashImplicitArrayWrapperBase> { }\n\n interface LoDashImplicitNillableArrayWrapper extends LoDashImplicitArrayWrapperBase> { }\n\n interface LoDashImplicitNumberArrayWrapperBase extends LoDashImplicitArrayWrapperBase { }\n\n interface LoDashImplicitNumberArrayWrapper extends LoDashImplicitArrayWrapper { }\n\n interface LoDashExplicitArrayWrapperBase extends LoDashExplicitWrapperBase {\n pop(): LoDashExplicitObjectWrapper;\n push(...items: T[]): TWrapper;\n shift(): LoDashExplicitObjectWrapper;\n sort(compareFn?: (a: T, b: T) => number): TWrapper;\n splice(start: number): TWrapper;\n splice(start: number, deleteCount: number, ...items: T[]): TWrapper;\n unshift(...items: T[]): TWrapper;\n }\n\n interface LoDashExplicitArrayWrapper extends LoDashExplicitArrayWrapperBase> { }\n\n interface LoDashExplicitNillableArrayWrapper extends LoDashExplicitArrayWrapperBase> { }\n\n interface LoDashExplicitNumberArrayWrapper extends LoDashExplicitArrayWrapper { }\n\n /*********\n * Array *\n *********/\n\n //_.chunk\n interface LoDashStatic {\n /**\n * Creates an array of elements split into groups the length of size. If collection can’t be split evenly, the\n * final chunk will be the remaining elements.\n *\n * @param array The array to process.\n * @param size The length of each chunk.\n * @return Returns the new array containing chunks.\n */\n chunk(\n array: List | null | undefined,\n size?: number\n ): T[][];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.chunk\n */\n chunk(size?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.chunk\n */\n chunk(size?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.chunk\n */\n chunk(size?: number): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.chunk\n */\n chunk(size?: number): LoDashExplicitArrayWrapper;\n }\n\n //_.compact\n interface LoDashStatic {\n /**\n * Creates an array with all falsey values removed. The values false, null, 0, \"\", undefined, and NaN are\n * falsey.\n *\n * @param array The array to compact.\n * @return (Array) Returns the new array of filtered values.\n */\n compact(array?: List | null | undefined): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.compact\n */\n compact(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.compact\n */\n compact(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.compact\n */\n compact(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.compact\n */\n compact(): LoDashExplicitArrayWrapper;\n }\n\n //_.concat DUMMY\n interface LoDashStatic {\n /**\n * Creates a new array concatenating `array` with any additional arrays\n * and/or values.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to concatenate.\n * @param {...*} [values] The values to concatenate.\n * @returns {Array} Returns the new concatenated array.\n * @example\n *\n * var array = [1];\n * var other = _.concat(array, 2, [3], [[4]]);\n *\n * console.log(other);\n * // => [1, 2, 3, [4]]\n *\n * console.log(array);\n * // => [1]\n */\n concat(array: List, ...values: Array>): T[];\n }\n\n //_.difference\n interface LoDashStatic {\n /**\n * Creates an array of unique array values not included in the other provided arrays using SameValueZero for\n * equality comparisons.\n *\n * @param array The array to inspect.\n * @param values The arrays of values to exclude.\n * @return Returns the new array of filtered values.\n */\n difference(\n array: List | null | undefined,\n ...values: Array>\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.difference\n */\n difference(...values: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.difference\n */\n difference(...values: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.difference\n */\n difference(...values: Array>): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.difference\n */\n difference(...values: Array>): LoDashExplicitArrayWrapper;\n }\n\n //_.differenceBy\n interface LoDashStatic {\n /**\n * This method is like _.difference except that it accepts iteratee which is invoked for each element of array\n * and values to generate the criterion by which uniqueness is computed. The iteratee is invoked with one\n * argument: (value).\n *\n * @param array The array to inspect.\n * @param values The values to exclude.\n * @param iteratee The iteratee invoked per element.\n * @returns Returns the new array of filtered values.\n */\n differenceBy(\n array: List | null | undefined,\n values?: List,\n iteratee?: ((value: T) => any)|string\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values?: List,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n iteratee?: ((value: T) => any)|string\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: ((value: T) => any)|string\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: ((value: T) => any)|string\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: ((value: T) => any)|string\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n array: List | null | undefined,\n ...values: any[]\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n ...values: any[]\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n ...values: any[]\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n ...values: any[]\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: ((value: T) => any)|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n values1?: List,\n values2?: List,\n values3?: List,\n values4?: List,\n values5?: List,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.differenceBy\n */\n differenceBy(\n ...values: any[]\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.differenceWith DUMMY\n interface LoDashStatic {\n /**\n * Creates an array of unique `array` values not included in the other\n * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.difference([3, 2, 1], [4, 2]);\n * // => [3, 1]\n */\n differenceWith(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.drop\n interface LoDashStatic {\n /**\n * Creates a slice of array with n elements dropped from the beginning.\n *\n * @param array The array to query.\n * @param n The number of elements to drop.\n * @return Returns the slice of array.\n */\n drop(array: List | null | undefined, n?: number): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.drop\n */\n drop(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.drop\n */\n drop(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.drop\n */\n drop(n?: number): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.drop\n */\n drop(n?: number): LoDashExplicitArrayWrapper;\n }\n\n //_.dropRight\n interface LoDashStatic {\n /**\n * Creates a slice of array with n elements dropped from the end.\n *\n * @param array The array to query.\n * @param n The number of elements to drop.\n * @return Returns the slice of array.\n */\n dropRight(\n array: List | null | undefined,\n n?: number\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.dropRight\n */\n dropRight(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.dropRight\n */\n dropRight(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.dropRight\n */\n dropRight(n?: number): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.dropRight\n */\n dropRight(n?: number): LoDashExplicitArrayWrapper;\n }\n\n //_.dropRightWhile\n interface LoDashStatic {\n /**\n * Creates a slice of array excluding elements dropped from the end. Elements are dropped until predicate\n * returns falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * match the properties of the given object, else false.\n *\n * @param array The array to query.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the slice of array.\n */\n dropRightWhile(\n array: List | null | undefined,\n predicate?: ListIterator\n ): TValue[];\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n array: List | null | undefined,\n predicate?: string\n ): TValue[];\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n array: List | null | undefined,\n predicate?: TWhere\n ): TValue[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropRightWhile\n */\n dropRightWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.dropWhile\n interface LoDashStatic {\n /**\n * Creates a slice of array excluding elements dropped from the beginning. Elements are dropped until predicate\n * returns falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param array The array to query.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the slice of array.\n */\n dropWhile(\n array: List | null | undefined,\n predicate?: ListIterator\n ): TValue[];\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n array: List | null | undefined,\n predicate?: string\n ): TValue[];\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n array: List | null | undefined,\n predicate?: TWhere\n ): TValue[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.dropWhile\n */\n dropWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.fill\n interface LoDashStatic {\n /**\n * Fills elements of array with value from start up to, but not including, end.\n *\n * Note: This method mutates array.\n *\n * @param array The array to fill.\n * @param value The value to fill array with.\n * @param start The start position.\n * @param end The end position.\n * @return Returns array.\n */\n fill(\n array: any[] | null | undefined,\n value: T,\n start?: number,\n end?: number\n ): T[];\n\n /**\n * @see _.fill\n */\n fill(\n array: List | null | undefined,\n value: T,\n start?: number,\n end?: number\n ): List;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.fill\n */\n fill(\n value: T,\n start?: number,\n end?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.fill\n */\n fill(\n value: T,\n start?: number,\n end?: number\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.fill\n */\n fill(\n value: T,\n start?: number,\n end?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.fill\n */\n fill(\n value: T,\n start?: number,\n end?: number\n ): LoDashExplicitObjectWrapper>;\n }\n\n //_.findIndex\n interface LoDashStatic {\n /**\n * This method is like _.find except that it returns the index of the first element predicate returns truthy\n * for instead of the element itself.\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param array The array to search.\n * @param predicate The function invoked per iteration.\n * @param fromIndex The index to search from.\n * @return Returns the index of the found element, else -1.\n */\n findIndex(\n array: List | null | undefined,\n predicate?: ListIterator,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n array: List | null | undefined,\n predicate?: string,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n array: List | null | undefined,\n predicate?: W,\n fromIndex?: number\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: string,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: W,\n fromIndex?: number\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: string,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: W,\n fromIndex?: number\n ): number;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: string,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: W,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: string,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findIndex\n */\n findIndex(\n predicate?: W,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n }\n\n //_.findLastIndex\n interface LoDashStatic {\n /**\n * This method is like _.findIndex except that it iterates over elements of collection from right to left.\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param array The array to search.\n * @param predicate The function invoked per iteration.\n * @param fromIndex The index to search from.\n * @return Returns the index of the found element, else -1.\n */\n findLastIndex(\n array: List | null | undefined,\n predicate?: ListIterator,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n array: List | null | undefined,\n predicate?: string,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n array: List | null | undefined,\n predicate?: W,\n fromIndex?: number\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: string,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: W,\n fromIndex?: number\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: string,\n fromIndex?: number\n ): number;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: W,\n fromIndex?: number\n ): number;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: string,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: W,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: ListIterator,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: string,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findLastIndex\n */\n findLastIndex(\n predicate?: W,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n }\n\n //_.first\n interface LoDashStatic {\n /**\n * @see _.head\n */\n first(array: List | null | undefined): T | undefined;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.head\n */\n first(): string | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.head\n */\n first(): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.head\n */\n first(): T | undefined;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.head\n */\n first(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.head\n */\n first(): T;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.head\n */\n first(): T;\n }\n\n interface RecursiveArray extends Array> {}\n interface ListOfRecursiveArraysOrValues extends List> {}\n\n //_.flatten\n interface LoDashStatic {\n /**\n * Flattens a nested array. If isDeep is true the array is recursively flattened, otherwise it’s only\n * flattened a single level.\n *\n * @param array The array to flatten.\n * @param isDeep Specify a deep flatten.\n * @return Returns the new flattened array.\n */\n flatten(array: ListOfRecursiveArraysOrValues | null | undefined, isDeep: boolean): T[];\n\n /**\n * @see _.flatten\n */\n flatten(array: List> | null | undefined): T[];\n\n /**\n * @see _.flatten\n */\n flatten(array: ListOfRecursiveArraysOrValues | null | undefined): RecursiveArray;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.flatten\n */\n flatten(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.flatten\n */\n flatten(isDeep?: boolean): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.flatten\n */\n flatten(isDeep?: boolean): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.flatten\n */\n flatten(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.flatten\n */\n flatten(isDeep?: boolean): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.flatten\n */\n flatten(isDeep?: boolean): LoDashExplicitArrayWrapper;\n }\n\n //_.flattenDeep\n interface LoDashStatic {\n /**\n * Recursively flattens a nested array.\n *\n * @param array The array to recursively flatten.\n * @return Returns the new flattened array.\n */\n flattenDeep(array: ListOfRecursiveArraysOrValues | null | undefined): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.flattenDeep\n */\n flattenDeep(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.flattenDeep\n */\n flattenDeep(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.flattenDeep\n */\n flattenDeep(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.flattenDeep\n */\n flattenDeep(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.flattenDeep\n */\n flattenDeep(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.flattenDeep\n */\n flattenDeep(): LoDashExplicitArrayWrapper;\n }\n\n // _.flattenDepth\n interface LoDashStatic {\n /**\n * Recursively flatten array up to depth times.\n *\n * @param array The array to recursively flatten.\n * @param number The maximum recursion depth.\n * @return Returns the new flattened array.\n */\n flattenDepth(array: ListOfRecursiveArraysOrValues | null | undefined, depth?: number): T[];\n }\n\n //_.fromPairs\n interface LoDashStatic {\n /**\n * The inverse of `_.toPairs`; this method returns an object composed\n * from key-value `pairs`.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} pairs The key-value pairs.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.fromPairs([['fred', 30], ['barney', 40]]);\n * // => { 'fred': 30, 'barney': 40 }\n */\n fromPairs(\n array: List<[_.StringRepresentable, T]> | null | undefined\n ): Dictionary;\n\n /**\n @see _.fromPairs\n */\n fromPairs(\n array: List | null | undefined\n ): Dictionary;\n }\n\n //_.fromPairs DUMMY\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.fromPairs\n */\n fromPairs(): LoDashImplicitObjectWrapper;\n }\n\n //_.fromPairs DUMMY\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.fromPairs\n */\n fromPairs(): LoDashExplicitObjectWrapper;\n }\n\n //_.head\n interface LoDashStatic {\n /**\n * Gets the first element of array.\n *\n * @alias _.first\n *\n * @param array The array to query.\n * @return Returns the first element of array.\n */\n head(array: List | null | undefined): T | undefined;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.head\n */\n head(): string | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.head\n */\n head(): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.head\n */\n head(): T | undefined;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.head\n */\n head(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.head\n */\n head(): T;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.head\n */\n head(): T;\n }\n\n //_.indexOf\n interface LoDashStatic {\n /**\n * Gets the index at which the first occurrence of `value` is found in `array`\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n * for equality comparisons. If `fromIndex` is negative, it's used as the offset\n * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`\n * performs a faster binary search.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to search.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.indexOf([1, 2, 1, 2], 2);\n * // => 1\n *\n * // using `fromIndex`\n * _.indexOf([1, 2, 1, 2], 2, 2);\n * // => 3\n */\n indexOf(\n array: List | null | undefined,\n value: T,\n fromIndex?: boolean|number\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.indexOf\n */\n indexOf(\n value: T,\n fromIndex?: boolean|number\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.indexOf\n */\n indexOf(\n value: TValue,\n fromIndex?: boolean|number\n ): number;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.indexOf\n */\n indexOf(\n value: T,\n fromIndex?: boolean|number\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.indexOf\n */\n indexOf(\n value: TValue,\n fromIndex?: boolean|number\n ): LoDashExplicitWrapper;\n }\n\n //_.intersectionBy DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.intersection` except that it accepts `iteratee`\n * which is invoked for each element of each `arrays` to generate the criterion\n * by which uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of shared values.\n * @example\n *\n * _.intersectionBy([2.1, 1.2], [4.3, 2.4], Math.floor);\n * // => [2.1]\n *\n * // using the `_.property` iteratee shorthand\n * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }]\n */\n intersectionBy(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.intersectionWith DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.intersection` except that it accepts `comparator`\n * which is invoked to compare elements of `arrays`. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of shared values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.intersectionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }]\n */\n intersectionWith(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.join\n interface LoDashStatic {\n /**\n * Converts all elements in `array` into a string separated by `separator`.\n *\n * @param array The array to convert.\n * @param separator The element separator.\n * @returns Returns the joined string.\n */\n join(\n array: List | null | undefined,\n separator?: string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.join\n */\n join(separator?: string): string;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.join\n */\n join(separator?: string): string;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.join\n */\n join(separator?: string): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.join\n */\n join(separator?: string): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.join\n */\n join(separator?: string): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.join\n */\n join(separator?: string): LoDashExplicitWrapper;\n }\n\n //_.pullAll DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.pull` except that it accepts an array of values to remove.\n *\n * **Note:** Unlike `_.difference`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3, 1, 2, 3];\n *\n * _.pull(array, [2, 3]);\n * console.log(array);\n * // => [1, 1]\n */\n pullAll(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.pullAllBy DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.pullAll` except that it accepts `iteratee` which is\n * invoked for each element of `array` and `values` to to generate the criterion\n * by which uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * **Note:** Unlike `_.differenceBy`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];\n *\n * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');\n * console.log(array);\n * // => [{ 'x': 2 }]\n */\n pullAllBy(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.reverse DUMMY\n interface LoDashStatic {\n /**\n * Reverses `array` so that the first element becomes the last, the second\n * element becomes the second to last, and so on.\n *\n * **Note:** This method mutates `array` and is based on\n * [`Array#reverse`](https://mdn.io/Array/reverse).\n *\n * @memberOf _\n * @category Array\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _.reverse(array);\n * // => [3, 2, 1]\n *\n * console.log(array);\n * // => [3, 2, 1]\n */\n reverse(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.sortedIndexOf\n interface LoDashStatic {\n /**\n * This method is like `_.indexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to search.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedIndexOf([1, 1, 2, 2], 2);\n * // => 2\n */\n sortedIndexOf(\n array: List | null | undefined,\n value: T\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortedIndexOf\n */\n sortedIndexOf(\n value: T\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sortedIndexOf\n */\n sortedIndexOf(\n value: TValue\n ): number;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortedIndexOf\n */\n sortedIndexOf(\n value: T\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortedIndexOf\n */\n sortedIndexOf(\n value: TValue\n ): LoDashExplicitWrapper;\n }\n\n //_.initial\n interface LoDashStatic {\n /**\n * Gets all but the last element of array.\n *\n * @param array The array to query.\n * @return Returns the slice of array.\n */\n initial(array: List | null | undefined): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.initial\n */\n initial(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.initial\n */\n initial(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.initial\n */\n initial(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.initial\n */\n initial(): LoDashExplicitArrayWrapper;\n }\n\n //_.intersection\n interface LoDashStatic {\n /**\n * Creates an array of unique values that are included in all of the provided arrays using SameValueZero for\n * equality comparisons.\n *\n * @param arrays The arrays to inspect.\n * @return Returns the new array of shared values.\n */\n intersection(...arrays: Array | null | undefined>): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.intersection\n */\n intersection(...arrays: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.intersection\n */\n intersection(...arrays: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.intersection\n */\n intersection(...arrays: Array>): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.intersection\n */\n intersection(...arrays: Array>): LoDashExplicitArrayWrapper;\n }\n\n //_.last\n interface LoDashStatic {\n /**\n * Gets the last element of array.\n *\n * @param array The array to query.\n * @return Returns the last element of array.\n */\n last(array: List | null | undefined): T | undefined;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.last\n */\n last(): string | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.last\n */\n last(): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.last\n */\n last(): T | undefined;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.last\n */\n last(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.last\n */\n last(): T;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.last\n */\n last(): T;\n }\n\n //_.lastIndexOf\n interface LoDashStatic {\n /**\n * This method is like _.indexOf except that it iterates over elements of array from right to left.\n *\n * @param array The array to search.\n * @param value The value to search for.\n * @param fromIndex The index to search from or true to perform a binary search on a sorted array.\n * @return Returns the index of the matched value, else -1.\n */\n lastIndexOf(\n array: List | null | undefined,\n value: T,\n fromIndex?: boolean|number\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.lastIndexOf\n */\n lastIndexOf(\n value: T,\n fromIndex?: boolean|number\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.lastIndexOf\n */\n lastIndexOf(\n value: TResult,\n fromIndex?: boolean|number\n ): number;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.lastIndexOf\n */\n lastIndexOf(\n value: T,\n fromIndex?: boolean|number\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.lastIndexOf\n */\n lastIndexOf(\n value: TResult,\n fromIndex?: boolean|number\n ): LoDashExplicitWrapper;\n }\n\n //_.nth\n interface LoDashStatic {\n /**\n * Gets the element at index `n` of `array`. If `n` is negative, the nth element from the end is returned.\n *\n * @param array array The array to query.\n * @param value The index of the element to return.\n * @return Returns the nth element of `array`.\n */\n nth(\n array: List | null | undefined,\n n?: number\n ): T | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.nth\n */\n nth(\n n?: number\n ): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.nth\n */\n nth(\n n?:number\n ): TResult | undefined;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.nth\n */\n nth(\n n?:number\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.nth\n */\n nth(\n n?:number\n ): LoDashExplicitWrapper;\n }\n\n //_.pull\n interface LoDashStatic {\n /**\n * Removes all provided values from array using SameValueZero for equality comparisons.\n *\n * Note: Unlike _.without, this method mutates array.\n *\n * @param array The array to modify.\n * @param values The values to remove.\n * @return Returns array.\n */\n pull(\n array: T[],\n ...values: T[]\n ): T[];\n\n /**\n * @see _.pull\n */\n pull(\n array: List,\n ...values: T[]\n ): List;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.pull\n */\n pull(...values: T[]): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.pull\n */\n pull(...values: TValue[]): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.pull\n */\n pull(...values: T[]): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.pull\n */\n pull(...values: TValue[]): LoDashExplicitObjectWrapper>;\n }\n\n //_.pullAt\n interface LoDashStatic {\n /**\n * Removes elements from array corresponding to the given indexes and returns an array of the removed elements.\n * Indexes may be specified as an array of indexes or as individual arguments.\n *\n * Note: Unlike _.at, this method mutates array.\n *\n * @param array The array to modify.\n * @param indexes The indexes of elements to remove, specified as individual indexes or arrays of indexes.\n * @return Returns the new array of removed elements.\n */\n pullAt(\n array: List,\n ...indexes: Array>\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.pullAt\n */\n pullAt(...indexes: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.pullAt\n */\n pullAt(...indexes: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.pullAt\n */\n pullAt(...indexes: Array>): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.pullAt\n */\n pullAt(...indexes: Array>): LoDashExplicitArrayWrapper;\n }\n\n //_.remove\n interface LoDashStatic {\n /**\n * Removes all elements from array that predicate returns truthy for and returns an array of the removed\n * elements. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * Note: Unlike _.filter, this method mutates array.\n *\n * @param array The array to modify.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the new array of removed elements.\n */\n remove(\n array: List,\n predicate?: ListIterator\n ): T[];\n\n /**\n * @see _.remove\n */\n remove(\n array: List,\n predicate?: string\n ): T[];\n\n /**\n * @see _.remove\n */\n remove(\n array: List,\n predicate?: W\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.remove\n */\n remove(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: W\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.remove\n */\n remove(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: W\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.remove\n */\n remove(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: W\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.remove\n */\n remove(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.remove\n */\n remove(\n predicate?: W\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.tail\n interface LoDashStatic {\n /**\n * Gets all but the first element of array.\n *\n * @alias _.tail\n *\n * @param array The array to query.\n * @return Returns the slice of array.\n */\n tail(array: List | null | undefined): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.tail\n */\n tail(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.tail\n */\n tail(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.tail\n */\n tail(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.tail\n */\n tail(): LoDashExplicitArrayWrapper;\n }\n\n //_.slice\n interface LoDashStatic {\n /**\n * Creates a slice of array from start up to, but not including, end.\n *\n * @param array The array to slice.\n * @param start The start position.\n * @param end The end position.\n * @return Returns the slice of array.\n */\n slice(\n array: T[] | null | undefined,\n start?: number,\n end?: number\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.slice\n */\n slice(\n start?: number,\n end?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.slice\n */\n slice(\n start?: number,\n end?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.sortedIndex\n interface LoDashStatic {\n /**\n * Uses a binary search to determine the lowest index at which `value` should\n * be inserted into `array` in order to maintain its sort order.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted into `array`.\n * @example\n *\n * _.sortedIndex([30, 50], 40);\n * // => 1\n *\n * _.sortedIndex([4, 5], 4);\n * // => 0\n */\n sortedIndex(\n array: List | null | undefined,\n value: T\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sortedIndex\n */\n sortedIndex(\n value: string\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortedIndex\n */\n sortedIndex(\n value: T\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sortedIndex\n */\n sortedIndex(\n value: T\n ): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sortedIndex\n */\n sortedIndex(\n value: string\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortedIndex\n */\n sortedIndex(\n value: T\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortedIndex\n */\n sortedIndex(\n value: T\n ): LoDashExplicitWrapper;\n }\n\n // _.sortedIndexBy\n interface LoDashStatic {\n /**\n * This method is like `_.sortedIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted into `array`.\n * @example\n *\n * var dict = { 'thirty': 30, 'forty': 40, 'fifty': 50 };\n *\n * _.sortedIndexBy(['thirty', 'fifty'], 'forty', _.propertyOf(dict));\n * // => 1\n *\n * // using the `_.property` iteratee shorthand\n * _.sortedIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');\n * // => 0\n */\n sortedIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: (x: T) => TSort\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: (x: T) => any\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: string\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: W\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: Object\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: string,\n iteratee: (x: string) => TSort\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: string\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: W\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: (x: T) => any\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: string\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: W\n ): number;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: Object\n ): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: string,\n iteratee: (x: string) => TSort\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: W\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: (x: T) => any\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: W\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedIndexBy\n */\n sortedIndexBy(\n value: T,\n iteratee: Object\n ): LoDashExplicitWrapper;\n }\n\n //_.sortedLastIndex\n interface LoDashStatic {\n /**\n * This method is like `_.sortedIndex` except that it returns the highest\n * index at which `value` should be inserted into `array` in order to\n * maintain its sort order.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted into `array`.\n * @example\n *\n * _.sortedLastIndex([4, 5], 4);\n * // => 1\n */\n sortedLastIndex(\n array: List | null | undefined,\n value: T\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sortedLastIndex\n */\n sortedLastIndex(\n value: string\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortedLastIndex\n */\n sortedLastIndex(\n value: T\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sortedLastIndex\n */\n sortedLastIndex(\n value: T\n ): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sortedLastIndex\n */\n sortedLastIndex(\n value: string\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortedLastIndex\n */\n sortedLastIndex(\n value: T\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortedLastIndex\n */\n sortedLastIndex(\n value: T\n ): LoDashExplicitWrapper;\n }\n\n //_.sortedLastIndexBy\n interface LoDashStatic {\n /**\n * This method is like `_.sortedLastIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted into `array`.\n * @example\n *\n * // using the `_.property` iteratee shorthand\n * _.sortedLastIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');\n * // => 1\n */\n sortedLastIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: (x: T) => TSort\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: (x: T) => any\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: string\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: W\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n array: List | null | undefined,\n value: T,\n iteratee: Object\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: string,\n iteratee: (x: string) => TSort\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: string\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: W\n ): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: (x: T) => any\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: string\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: W\n ): number;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: Object\n ): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: string,\n iteratee: (x: string) => TSort\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: W\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: (x: T) => TSort\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: (x: T) => any\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: W\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sortedLastIndexBy\n */\n sortedLastIndexBy(\n value: T,\n iteratee: Object\n ): LoDashExplicitWrapper;\n }\n\n //_.sortedLastIndexOf DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.lastIndexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to search.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedLastIndexOf([1, 1, 2, 2], 2);\n * // => 3\n */\n sortedLastIndexOf(\n array: List | null | undefined,\n value: T\n ): number;\n }\n\n //_.tail\n interface LoDashStatic {\n /**\n * @see _.rest\n */\n tail(array: List | null | undefined): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.rest\n */\n tail(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.rest\n */\n tail(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.rest\n */\n tail(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.rest\n */\n tail(): LoDashExplicitArrayWrapper;\n }\n\n //_.take\n interface LoDashStatic {\n /**\n * Creates a slice of array with n elements taken from the beginning.\n *\n * @param array The array to query.\n * @param n The number of elements to take.\n * @return Returns the slice of array.\n */\n take(\n array: List | null | undefined,\n n?: number\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.take\n */\n take(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.take\n */\n take(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.take\n */\n take(n?: number): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.take\n */\n take(n?: number): LoDashExplicitArrayWrapper;\n }\n\n //_.takeRight\n interface LoDashStatic {\n /**\n * Creates a slice of array with n elements taken from the end.\n *\n * @param array The array to query.\n * @param n The number of elements to take.\n * @return Returns the slice of array.\n */\n takeRight(\n array: List | null | undefined,\n n?: number\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.takeRight\n */\n takeRight(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.takeRight\n */\n takeRight(n?: number): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.takeRight\n */\n takeRight(n?: number): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.takeRight\n */\n takeRight(n?: number): LoDashExplicitArrayWrapper;\n }\n\n //_.takeRightWhile\n interface LoDashStatic {\n /**\n * Creates a slice of array with elements taken from the end. Elements are taken until predicate returns\n * falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param array The array to query.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the slice of array.\n */\n takeRightWhile(\n array: List | null | undefined,\n predicate?: ListIterator\n ): TValue[];\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n array: List | null | undefined,\n predicate?: string\n ): TValue[];\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n array: List | null | undefined,\n predicate?: TWhere\n ): TValue[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeRightWhile\n */\n takeRightWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.takeWhile\n interface LoDashStatic {\n /**\n * Creates a slice of array with elements taken from the beginning. Elements are taken until predicate returns\n * falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param array The array to query.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the slice of array.\n */\n takeWhile(\n array: List | null | undefined,\n predicate?: ListIterator\n ): TValue[];\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n array: List | null | undefined,\n predicate?: string\n ): TValue[];\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n array: List | null | undefined,\n predicate?: TWhere\n ): TValue[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.takeWhile\n */\n takeWhile(\n predicate?: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.union\n interface LoDashStatic {\n /**\n * Creates an array of unique values, in order, from all of the provided arrays using SameValueZero for\n * equality comparisons.\n *\n * @param arrays The arrays to inspect.\n * @return Returns the new array of combined values.\n */\n union(...arrays: Array | null | undefined>): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.union\n */\n union(...arrays: Array | null | undefined>): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.union\n */\n union(...arrays: Array | null | undefined>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.union\n */\n union(...arrays: Array | null | undefined>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.union\n */\n union(...arrays: Array | null | undefined>): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.union\n */\n union(...arrays: Array | null | undefined>): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.union\n */\n union(...arrays: Array | null | undefined>): LoDashExplicitArrayWrapper;\n }\n\n //_.unionBy\n interface LoDashStatic {\n /**\n * This method is like `_.union` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by which\n * uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * @param arrays The arrays to inspect.\n * @param iteratee The iteratee invoked per element.\n * @return Returns the new array of combined values.\n */\n unionBy(\n arrays: List | null | undefined,\n iteratee?: (value: T) => any\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays: List | null | undefined,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n iteratee?: (value: T) => any\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: (value: T) => any\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: (value: T) => any\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: (value: T) => any\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays1: List | null | undefined,\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: W\n ): T[];\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays: List | null | undefined,\n ...iteratee: any[]\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n ...iteratee: any[]\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: W\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n ...iteratee: any[]\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n ...iteratee: any[]\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: (value: T) => any\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n arrays2: List | null | undefined,\n arrays3: List | null | undefined,\n arrays4: List | null | undefined,\n arrays5: List | null | undefined,\n iteratee?: W\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.unionBy\n */\n unionBy(\n ...iteratee: any[]\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.uniq\n interface LoDashStatic {\n /**\n * Creates a duplicate-free version of an array, using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n * for equality comparisons, in which only the first occurrence of each element\n * is kept.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniq([2, 1, 2]);\n * // => [2, 1]\n */\n uniq(\n array: List | null | undefined\n ): T[];\n\n /**\n * @see _.uniq\n */\n uniq(\n array: List | null | undefined\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.uniq\n */\n uniq(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.uniq\n */\n uniq(): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniq\n */\n uniq(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n uniq(): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniq\n */\n uniq(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.uniq\n */\n uniq(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.uniq\n */\n uniq(): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniq\n */\n uniq(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.uniq\n */\n uniq(): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniq\n */\n uniq(): LoDashExplicitArrayWrapper;\n }\n\n //_.uniqBy\n interface LoDashStatic {\n /**\n * This method is like `_.uniq` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniqBy([2.1, 1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // using the `_.property` iteratee shorthand\n * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n */\n uniqBy(\n array: List | null | undefined,\n iteratee: ListIterator\n ): T[];\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n array: List | null | undefined,\n iteratee: ListIterator\n ): T[];\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n array: List | null | undefined,\n iteratee: string\n ): T[];\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n array: List | null | undefined,\n iteratee: Object\n ): T[];\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n array: List | null | undefined,\n iteratee: TWhere\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: Object\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: Object\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.uniqBy\n */\n uniqBy(\n iteratee: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.sortedUniq\n interface LoDashStatic {\n /**\n * This method is like `_.uniq` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniq([1, 1, 2]);\n * // => [1, 2]\n */\n sortedUniq(\n array: List | null | undefined\n ): T[];\n\n /**\n * @see _.sortedUniq\n */\n sortedUniq(\n array: List | null | undefined\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n sortedUniq(): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniq\n */\n sortedUniq(): LoDashExplicitArrayWrapper;\n }\n\n //_.sortedUniqBy\n interface LoDashStatic {\n /**\n * This method is like `_.uniqBy` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);\n * // => [1.1, 2.2]\n */\n sortedUniqBy(\n array: List | null | undefined,\n iteratee: ListIterator\n ): T[];\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n array: List | null | undefined,\n iteratee: ListIterator\n ): T[];\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n array: List | null | undefined,\n iteratee: string\n ): T[];\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n array: List | null | undefined,\n iteratee: Object\n ): T[];\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n array: List | null | undefined,\n iteratee: TWhere\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: Object\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: TWhere\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: Object\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortedUniqBy\n */\n sortedUniqBy(\n iteratee: TWhere\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.unionWith DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.union` except that it accepts `comparator` which\n * is invoked to compare elements of `arrays`. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.unionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n unionWith(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.uniqWith DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.uniq` except that it accepts `comparator` which\n * is invoked to compare elements of `array`. The comparator is invoked with\n * two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.uniqWith(objects, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]\n */\n uniqWith(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.unzip\n interface LoDashStatic {\n /**\n * This method is like _.zip except that it accepts an array of grouped elements and creates an array\n * regrouping the elements to their pre-zip configuration.\n *\n * @param array The array of grouped elements to process.\n * @return Returns the new array of regrouped elements.\n */\n unzip(array: List> | null | undefined): T[][];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.unzip\n */\n unzip(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.unzip\n */\n unzip(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.unzip\n */\n unzip(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.unzip\n */\n unzip(): LoDashExplicitArrayWrapper;\n }\n\n //_.unzipWith\n interface LoDashStatic {\n /**\n * This method is like _.unzip except that it accepts an iteratee to specify how regrouped values should be\n * combined. The iteratee is bound to thisArg and invoked with four arguments: (accumulator, value, index,\n * group).\n *\n * @param array The array of grouped elements to process.\n * @param iteratee The function to combine regrouped values.\n * @param thisArg The this binding of iteratee.\n * @return Returns the new array of regrouped elements.\n */\n unzipWith(\n array: List> | null | undefined,\n iteratee?: MemoIterator\n ): TResult[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.unzipWith\n */\n unzipWith(\n iteratee?: MemoIterator\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.unzipWith\n */\n unzipWith(\n iteratee?: MemoIterator\n ): LoDashImplicitArrayWrapper;\n }\n\n //_.without\n interface LoDashStatic {\n /**\n * Creates an array excluding all provided values using SameValueZero for equality comparisons.\n *\n * @param array The array to filter.\n * @param values The values to exclude.\n * @return Returns the new array of filtered values.\n */\n without(\n array: List | null | undefined,\n ...values: T[]\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.without\n */\n without(...values: T[]): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.without\n */\n without(...values: T[]): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.without\n */\n without(...values: T[]): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.without\n */\n without(...values: T[]): LoDashExplicitArrayWrapper;\n }\n\n //_.xor\n interface LoDashStatic {\n /**\n * Creates an array of unique values that is the symmetric difference of the provided arrays.\n *\n * @param arrays The arrays to inspect.\n * @return Returns the new array of values.\n */\n xor(...arrays: Array | null | undefined>): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.xor\n */\n xor(...arrays: Array | null | undefined>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.xor\n */\n xor(...arrays: Array | null | undefined>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.xor\n */\n xor(...arrays: Array | null | undefined>): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.xor\n */\n xor(...arrays: Array | null | undefined>): LoDashExplicitArrayWrapper;\n }\n\n //_.xorBy DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.xor` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by which\n * uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of values.\n * @example\n *\n * _.xorBy([2.1, 1.2], [4.3, 2.4], Math.floor);\n * // => [1.2, 4.3]\n *\n * // using the `_.property` iteratee shorthand\n * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 2 }]\n */\n xorBy(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.xorWith DUMMY\n interface LoDashStatic {\n /**\n * This method is like `_.xor` except that it accepts `comparator` which is\n * invoked to compare elements of `arrays`. The comparator is invoked with\n * two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.xorWith(objects, others, _.isEqual);\n * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n xorWith(\n array: List,\n ...values: any[]\n ): any[];\n }\n\n //_.zip\n interface LoDashStatic {\n /**\n * Creates an array of grouped elements, the first of which contains the first elements of the given arrays,\n * the second of which contains the second elements of the given arrays, and so on.\n *\n * @param arrays The arrays to process.\n * @return Returns the new array of grouped elements.\n */\n zip(...arrays: Array | null | undefined>): T[][];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.zip\n */\n zip(...arrays: Array | null | undefined>): _.LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.zip\n */\n zip(...arrays: Array | null | undefined>): _.LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.zip\n */\n zip(...arrays: Array | null | undefined>): _.LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.zip\n */\n zip(...arrays: Array | null | undefined>): _.LoDashExplicitArrayWrapper;\n }\n\n //_.zipObject\n interface LoDashStatic {\n /**\n * This method is like _.fromPairs except that it accepts two arrays, one of property\n * identifiers and one of corresponding values.\n *\n * @param props The property names.\n * @param values The property values.\n * @return Returns the new object.\n */\n zipObject(\n props: List|List>,\n values?: List\n ): TResult;\n /**\n * This method is like _.zipObject except that it supports property paths.\n *\n * @param props The property names.\n * @param values The property values.\n * @return Returns the new object.\n */\n zipObjectDeep(\n props: List|List>,\n values?: List\n ): TResult;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n props: List|List>,\n values?: List\n ): TResult;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n props: List|List>,\n values?: List\n ): TResult;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n props: List|List>,\n values?: List\n ): _.Dictionary;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n props: List|List>,\n values?: List\n ): _.Dictionary;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashImplicitObjectWrapper<_.Dictionary>;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashImplicitObjectWrapper<_.Dictionary>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashImplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashImplicitObjectWrapper<_.Dictionary>;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashImplicitObjectWrapper<_.Dictionary>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashExplicitObjectWrapper<_.Dictionary>;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashExplicitObjectWrapper<_.Dictionary>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashExplicitObjectWrapper;\n\n /**\n * @see _.zipObject\n */\n zipObject(\n values?: List\n ): _.LoDashExplicitObjectWrapper<_.Dictionary>;\n /**\n * @see _.zipObjectDeep\n */\n zipObjectDeep(\n values?: List\n ): _.LoDashExplicitObjectWrapper<_.Dictionary>;\n }\n\n //_.zipWith\n interface LoDashStatic {\n /**\n * This method is like _.zip except that it accepts an iteratee to specify how grouped values should be\n * combined. The iteratee is bound to thisArg and invoked with four arguments: (accumulator, value, index,\n * group).\n * @param {...Array} [arrays] The arrays to process.\n * @param {Function} [iteratee] The function to combine grouped values.\n * @param {*} [thisArg] The `this` binding of `iteratee`.\n * @return Returns the new array of grouped elements.\n */\n zipWith(...args: any[]): TResult[];\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.zipWith\n */\n zipWith(...args: any[]): LoDashImplicitArrayWrapper;\n }\n\n /*********\n * Chain *\n *********/\n\n //_.chain\n interface LoDashStatic {\n /**\n * Creates a lodash object that wraps value with explicit method chaining enabled.\n *\n * @param value The value to wrap.\n * @return Returns the new lodash wrapper instance.\n */\n chain(value: number): LoDashExplicitWrapper;\n chain(value: string): LoDashExplicitWrapper;\n chain(value: boolean): LoDashExplicitWrapper;\n chain(value: null | undefined): LoDashExplicitWrapper;\n chain(value: T[]): LoDashExplicitArrayWrapper;\n chain(value: ReadonlyArray): LoDashExplicitArrayWrapper;\n chain(value: T[] | null | undefined): LoDashExplicitNillableArrayWrapper;\n chain(value: ReadonlyArray | null | undefined): LoDashExplicitNillableArrayWrapper;\n chain(value: T): LoDashExplicitObjectWrapper;\n chain(value: T | null | undefined): LoDashExplicitObjectWrapper;\n chain(value: any): LoDashExplicitWrapper;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.chain\n */\n chain(): LoDashExplicitWrapper;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.chain\n */\n chain(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashImplicitNillableArrayWrapper {\n /**\n * @see _.chain\n */\n chain(): LoDashExplicitNillableArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.chain\n */\n chain(): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashImplicitNillableObjectWrapper {\n /**\n * @see _.chain\n */\n chain(): LoDashExplicitNillableObjectWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.chain\n */\n chain(): TWrapper;\n }\n\n //_.tap\n interface LoDashStatic {\n /**\n * This method invokes interceptor and returns value. The interceptor is bound to thisArg and invoked with one\n * argument; (value). The purpose of this method is to \"tap into\" a method chain in order to perform operations\n * on intermediate results within the chain.\n *\n * @param value The value to provide to interceptor.\n * @param interceptor The function to invoke.\n * @parem thisArg The this binding of interceptor.\n * @return Returns value.\n **/\n tap(\n value: T,\n interceptor: (value: T) => void\n ): T;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.tap\n */\n tap(\n interceptor: (value: T) => void\n ): TWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.tap\n */\n tap(\n interceptor: (value: T) => void\n ): TWrapper;\n }\n\n //_.thru\n interface LoDashStatic {\n /**\n * This method is like _.tap except that it returns the result of interceptor.\n *\n * @param value The value to provide to interceptor.\n * @param interceptor The function to invoke.\n * @param thisArg The this binding of interceptor.\n * @return Returns the result of interceptor.\n */\n thru(\n value: T,\n interceptor: (value: T) => TResult\n ): TResult;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult): LoDashImplicitWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult): LoDashImplicitWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult): LoDashImplicitWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult[]): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.thru\n */\n thru(\n interceptor: (value: T) => TResult[]\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.prototype.commit\n interface LoDashImplicitWrapperBase {\n /**\n * Executes the chained sequence and returns the wrapped result.\n *\n * @return Returns the new lodash wrapper instance.\n */\n commit(): TWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.commit\n */\n commit(): TWrapper;\n }\n\n //_.prototype.concat\n interface LoDashImplicitWrapperBase {\n /**\n * Creates a new array joining a wrapped array with any additional arrays and/or values.\n *\n * @param items\n * @return Returns the new concatenated array.\n */\n concat(...items: Array>): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.concat\n */\n concat(...items: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.concat\n */\n concat(...items: Array>): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.concat\n */\n concat(...items: Array>): LoDashExplicitArrayWrapper;\n }\n\n //_.prototype.plant\n interface LoDashImplicitWrapperBase {\n /**\n * Creates a clone of the chained sequence planting value as the wrapped value.\n * @param value The value to plant as the wrapped value.\n * @return Returns the new lodash wrapper instance.\n */\n plant(value: number): LoDashImplicitWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: string): LoDashImplicitStringWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: boolean): LoDashImplicitWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: number[]): LoDashImplicitNumberArrayWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: T[]): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: ReadonlyArray): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: T): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: any): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.plant\n */\n plant(value: number): LoDashExplicitWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: string): LoDashExplicitStringWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: boolean): LoDashExplicitWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: number[]): LoDashExplicitNumberArrayWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: T[]): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: ReadonlyArray): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: T): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.plant\n */\n plant(value: any): LoDashExplicitWrapper;\n }\n\n //_.prototype.reverse\n interface LoDashImplicitArrayWrapper {\n /**\n * Reverses the wrapped array so the first element becomes the last, the second element becomes the second to\n * last, and so on.\n *\n * Note: This method mutates the wrapped array.\n *\n * @return Returns the new reversed lodash wrapper instance.\n */\n reverse(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.reverse\n */\n reverse(): LoDashExplicitArrayWrapper;\n }\n\n //_.prototype.toJSON\n interface LoDashWrapperBase {\n /**\n * @see _.value\n */\n toJSON(): T;\n }\n\n //_.prototype.toString\n interface LoDashWrapperBase {\n /**\n * Produces the result of coercing the unwrapped value to a string.\n *\n * @return Returns the coerced string value.\n */\n toString(): string;\n }\n\n //_.prototype.value\n interface LoDashWrapperBase {\n /**\n * Executes the chained sequence to extract the unwrapped value.\n *\n * @alias _.toJSON, _.valueOf\n *\n * @return Returns the resolved unwrapped value.\n */\n value(): T;\n }\n\n //_.valueOf\n interface LoDashWrapperBase {\n /**\n * @see _.value\n */\n valueOf(): T;\n }\n\n /**************\n * Collection *\n **************/\n\n //_.at\n interface LoDashStatic {\n /**\n * Creates an array of elements corresponding to the given keys, or indexes, of collection. Keys may be\n * specified as individual arguments or as arrays of keys.\n *\n * @param collection The collection to iterate over.\n * @param props The property names or indexes of elements to pick, specified individually or in arrays.\n * @return Returns the new array of picked elements.\n */\n at(\n collection: List|Dictionary | null | undefined,\n ...props: Array>\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.at\n */\n at(...props: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.at\n */\n at(...props: Array>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.at\n */\n at(...props: Array>): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.at\n */\n at(...props: Array>): LoDashExplicitArrayWrapper;\n }\n\n //_.countBy\n interface LoDashStatic {\n /**\n * Creates an object composed of keys generated from the results of running each element of collection through\n * iteratee. The corresponding value of each key is the number of times the key was returned by iteratee. The\n * iteratee is bound to thisArg and invoked with three arguments:\n * (value, index|key, collection).\n *\n * If a property name is provided for iteratee the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for iteratee the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param collection The collection to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns the composed aggregate object.\n */\n countBy(\n collection: List | null | undefined,\n iteratee?: ListIterator\n ): Dictionary;\n\n /**\n * @see _.countBy\n */\n countBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.countBy\n */\n countBy(\n collection: NumericDictionary | null | undefined,\n iteratee?: NumericDictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.countBy\n */\n countBy(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n iteratee?: string\n ): Dictionary;\n\n /**\n * @see _.countBy\n */\n countBy(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n iteratee?: W\n ): Dictionary;\n\n /**\n * @see _.countBy\n */\n countBy(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n iteratee?: Object\n ): Dictionary;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: ListIterator\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: ListIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: W\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: ListIterator|DictionaryIterator|NumericDictionaryIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: W\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: ListIterator\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: ListIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: W\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: ListIterator|DictionaryIterator|NumericDictionaryIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.countBy\n */\n countBy(\n iteratee?: W\n ): LoDashExplicitObjectWrapper>;\n }\n\n //_.each\n interface LoDashStatic {\n each: typeof _.forEach;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.forEach\n */\n each(\n iteratee: StringIterator\n ): LoDashImplicitWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.forEach\n */\n each(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forEach\n */\n each(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.forEach\n */\n each(\n iteratee: StringIterator\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.forEach\n */\n each(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forEach\n */\n each(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n //_.eachRight\n interface LoDashStatic {\n eachRight: typeof _.forEachRight;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.forEachRight\n */\n eachRight(\n iteratee: StringIterator\n ): LoDashImplicitWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.forEachRight\n */\n eachRight(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forEachRight\n */\n eachRight(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.forEachRight\n */\n eachRight(\n iteratee: StringIterator\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.forEachRight\n */\n eachRight(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forEachRight\n */\n eachRight(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n //_.every\n interface LoDashStatic {\n /**\n * Checks if predicate returns truthy for all elements of collection. Iteration is stopped once predicate\n * returns falsey. The predicate is invoked with three arguments: (value, index|key, collection).\n *\n * @param collection The collection to iterate over.\n * @param predicate The function invoked per iteration.\n * @return Returns true if all elements pass the predicate check, else false.\n */\n every(\n collection: List | null | undefined,\n predicate?: ListIterator\n ): boolean;\n\n /**\n * @see _.every\n */\n every(\n collection: Dictionary | null | undefined,\n predicate?: DictionaryIterator\n ): boolean;\n\n /**\n * @see _.every\n */\n every(\n collection: NumericDictionary | null | undefined,\n predicate?: NumericDictionaryIterator\n ): boolean;\n\n /**\n * @see _.every\n */\n every(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n predicate?: string|any[]|PartialObject\n ): boolean;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.every\n */\n every(\n predicate?: ListIterator|NumericDictionaryIterator\n ): boolean;\n\n /**\n * @see _.every\n */\n every(\n predicate?: string|any[]\n ): boolean;\n\n /**\n * @see _.every\n */\n every(\n predicate?: PartialObject\n ): boolean;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.every\n */\n every(\n predicate?: ListIterator|DictionaryIterator|NumericDictionaryIterator\n ): boolean;\n\n /**\n * @see _.every\n */\n every(\n predicate?: string|any[]\n ): boolean;\n\n /**\n * @see _.every\n */\n every(\n predicate?: PartialObject\n ): boolean;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.every\n */\n every(\n predicate?: ListIterator|NumericDictionaryIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.every\n */\n every(\n predicate?: string|any[]\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.every\n */\n every(\n predicate?: PartialObject\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.every\n */\n every(\n predicate?: ListIterator|DictionaryIterator|NumericDictionaryIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.every\n */\n every(\n predicate?: string|any[]\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.every\n */\n every(\n predicate?: PartialObject\n ): LoDashExplicitWrapper;\n }\n\n //_.filter\n interface LoDashStatic {\n /**\n * Iterates over elements of collection, returning an array of all elements predicate returns truthy for. The\n * predicate is bound to thisArg and invoked with three arguments: (value, index|key, collection).\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param collection The collection to iterate over.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the new filtered array.\n */\n filter(\n collection: List | null | undefined,\n predicate: ListIteratorTypeGuard\n ): S[];\n\n /**\n * @see _.filter\n */\n filter(\n collection: List | null | undefined,\n predicate?: ListIterator\n ): T[];\n\n /**\n * @see _.filter\n */\n filter(\n collection: Dictionary | null | undefined,\n predicate: DictionaryIteratorTypeGuard\n ): S[];\n\n /**\n * @see _.filter\n */\n filter(\n collection: Dictionary | null | undefined,\n predicate?: DictionaryIterator\n ): T[];\n\n /**\n * @see _.filter\n */\n filter(\n collection: string | null | undefined,\n predicate?: StringIterator\n ): string[];\n\n /**\n * @see _.filter\n */\n filter(\n collection: List|Dictionary | null | undefined,\n predicate: string | [string, any] | RegExp | PartialObject\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.filter\n */\n filter(\n predicate?: StringIterator\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIteratorTypeGuard\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIterator | string | [string, any] | RegExp | PartialObject\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIteratorTypeGuard\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIterator | DictionaryIterator | string | [string, any] | RegExp | PartialObject\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.filter\n */\n filter(\n predicate?: StringIterator\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIteratorTypeGuard\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIterator | string | [string, any] | RegExp | PartialObject\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIteratorTypeGuard\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.filter\n */\n filter(\n predicate: ListIterator | DictionaryIterator | string | [string, any] | RegExp | PartialObject\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.find\n interface LoDashStatic {\n /**\n * Iterates over elements of collection, returning the first element predicate returns truthy for.\n * The predicate is bound to thisArg and invoked with three arguments: (value, index|key, collection).\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param collection The collection to search.\n * @param predicate The function invoked per iteration.\n * @param fromIndex The index to search from.\n * @return Returns the matched element, else undefined.\n */\n find(\n collection: List | null | undefined,\n predicate: ListIteratorTypeGuard,\n fromIndex?: number\n ): S|undefined;\n \n /**\n * @see _.find\n */\n find(\n collection: List | null | undefined,\n predicate?: ListIterator,\n fromIndex?: number\n ): T|undefined;\n\n /**\n * @see _.find\n */\n find(\n collection: Dictionary | null | undefined,\n predicate: DictionaryIteratorTypeGuard,\n fromIndex?: number\n ): S|undefined;\n\n /**\n * @see _.find\n */\n find(\n collection: Dictionary | null | undefined,\n predicate?: DictionaryIterator,\n fromIndex?: number\n ): T|undefined;\n\n /**\n * @see _.find\n */\n find(\n collection: List|Dictionary | null | undefined,\n predicate?: string | PartialObject | [string, any],\n fromIndex?: number\n ): T|undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.find\n */\n find(\n predicate: ListIteratorTypeGuard,\n fromIndex?: number\n ): S|undefined;\n\n /**\n * @see _.find\n */\n find(\n predicate?: ListIterator | string | PartialObject | [string, any],\n fromIndex?: number\n ): T|undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.find\n */\n find(\n predicate?: ListIterator | DictionaryIterator | string | PartialObject | [string, any],\n fromIndex?: number\n ): TResult|undefined;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.find\n */\n find(\n predicate?: ListIterator | string | PartialObject | [string, any],\n fromIndex?: number\n ): any;\n }\n\n //_.findLast\n interface LoDashStatic {\n /**\n * This method is like _.find except that it iterates over elements of a collection from\n * right to left.\n * @param collection Searches for a value in this list.\n * @param predicate The function called per iteration.\n * @param fromIndex The index to search from.\n * @return The found element, else undefined.\n **/\n findLast(\n collection: List | null | undefined,\n predicate: ListIteratorTypeGuard,\n fromIndex?: number\n ): S|undefined;\n \n /**\n * @see _.findLast\n */\n findLast(\n collection: List | null | undefined,\n predicate?: ListIterator,\n fromIndex?: number\n ): T|undefined;\n\n /**\n * @see _.findLast\n */\n findLast(\n collection: Dictionary | null | undefined,\n predicate: DictionaryIteratorTypeGuard,\n fromIndex?: number\n ): S|undefined;\n\n /**\n * @see _.findLast\n */\n findLast(\n collection: Dictionary | null | undefined,\n predicate?: DictionaryIterator,\n fromIndex?: number\n ): T|undefined;\n\n /**\n * @see _.findLast\n */\n findLast(\n collection: List|Dictionary | null | undefined,\n predicate?: string | PartialObject | [string, any],\n fromIndex?: number\n ): T|undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.findLast\n */\n findLast(\n predicate: ListIteratorTypeGuard,\n fromIndex?: number\n ): S|undefined;\n\n /**\n * @see _.findLast\n */\n findLast(\n predicate?: ListIterator | string | PartialObject | [string, any],\n fromIndex?: number\n ): T|undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.findLast\n */\n findLast(\n predicate?: ListIterator | DictionaryIterator | string | PartialObject | [string, any],\n fromIndex?: number\n ): TResult|undefined;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.findLast\n */\n findLast(\n predicate?: ListIterator | string | PartialObject | [string, any],\n fromIndex?: number\n ): any;\n }\n\n //_.flatMap\n interface LoDashStatic {\n /**\n * Creates an array of flattened values by running each element in collection through iteratee\n * and concating its result to the other mapped values. The iteratee is invoked with three arguments:\n * (value, index|key, collection).\n *\n * @param collection The collection to iterate over.\n * @param iteratee The function invoked per iteration.\n * @return Returns the new flattened array.\n */\n flatMap(\n collection: List> | Dictionary> | NumericDictionary> | null | undefined\n ): T[];\n\n /**\n * @see _.flatMap\n */\n flatMap(\n collection: List | null | undefined,\n iteratee: ListIterator> | string\n ): TResult[];\n\n /**\n * @see _.flatMap\n */\n flatMap(\n collection: Dictionary | null | undefined,\n iteratee: DictionaryIterator> | string\n ): TResult[];\n\n /**\n * @see _.flatMap\n */\n flatMap(\n collection: NumericDictionary | null | undefined,\n iteratee: NumericDictionaryIterator> | string\n ): TResult[];\n\n /**\n * @see _.flatMap\n */\n flatMap(\n collection: object | null | undefined,\n iteratee?: ObjectIterator> | string\n ): TResult[];\n\n /**\n * @see _.flatMap\n */\n flatMap(\n collection: object | null | undefined,\n iteratee: object\n ): boolean[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: StringIterator>\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: ListIterator>|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: object\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: ListIterator>|DictionaryIterator>|NumericDictionaryIterator>\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: ObjectIterator>|string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: object\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: StringIterator>\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: ListIterator>|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: object\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: ListIterator>|DictionaryIterator>|NumericDictionaryIterator>\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: ObjectIterator>|string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(\n iteratee: object\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.flatMap\n */\n flatMap(): LoDashExplicitArrayWrapper;\n }\n\n //_.forEach\n interface LoDashStatic {\n /**\n * Iterates over elements of collection invoking iteratee for each element. The iteratee is bound to thisArg\n * and invoked with three arguments:\n * (value, index|key, collection). Iteratee functions may exit iteration early by explicitly returning false.\n *\n * Note: As with other \"Collections\" methods, objects with a \"length\" property are iterated like arrays. To\n * avoid this behavior _.forIn or _.forOwn may be used for object iteration.\n *\n * @alias _.each\n *\n * @param collection The collection to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n */\n forEach(\n collection: TString,\n iteratee?: StringIterator\n ): TString;\n\n /**\n * @see _.forEach\n */\n forEach | null | undefined>(\n collection: TList & (List | null | undefined),\n iteratee?: ListIterator\n ): TList;\n\n /**\n * @see _.forEach\n */\n forEach | null | undefined>(\n collection: TDictionary & (Dictionary | null | undefined),\n iteratee?: DictionaryIterator\n ): TDictionary;\n\n /**\n * @see _.forEach\n */\n forEach(\n collection: T,\n iteratee?: ObjectIterator\n ): T;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.forEach\n */\n forEach(\n iteratee: StringIterator\n ): LoDashImplicitWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.forEach\n */\n forEach(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forEach\n */\n forEach(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.forEach\n */\n forEach(\n iteratee: StringIterator\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.forEach\n */\n forEach(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forEach\n */\n forEach(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n //_.forEachRight\n interface LoDashStatic {\n /**\n * This method is like _.forEach except that it iterates over elements of collection from right to left.\n *\n * @alias _.eachRight\n *\n * @param collection The collection to iterate over.\n * @param iteratee The function called per iteration.\n * @param thisArg The this binding of callback.\n */\n forEachRight(\n collection: TString,\n iteratee?: StringIterator\n ): TString;\n\n /**\n * @see _.forEachRight\n */\n forEachRight | null | undefined>(\n collection: TList & (List | null | undefined),\n iteratee?: ListIterator\n ): TList;\n\n /**\n * @see _.forEachRight\n */\n forEachRight | null | undefined>(\n collection: TDictionary & (Dictionary | null | undefined),\n iteratee?: DictionaryIterator\n ): TDictionary;\n\n /**\n * @see _.forEachRight\n */\n forEachRight(\n collection: T,\n iteratee?: ObjectIterator\n ): T;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.forEachRight\n */\n forEachRight(\n iteratee: StringIterator\n ): LoDashImplicitWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.forEachRight\n */\n forEachRight(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forEachRight\n */\n forEachRight(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.forEachRight\n */\n forEachRight(\n iteratee: StringIterator\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.forEachRight\n */\n forEachRight(\n iteratee: ListIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forEachRight\n */\n forEachRight(\n iteratee?: ListIterator|DictionaryIterator\n ): TWrapper;\n }\n\n //_.groupBy\n interface LoDashStatic {\n /**\n * Creates an object composed of keys generated from the results of running each element of collection through\n * iteratee. The corresponding value of each key is an array of the elements responsible for generating the\n * key. The iteratee is bound to thisArg and invoked with three arguments:\n * (value, index|key, collection).\n *\n * If a property name is provided for iteratee the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for iteratee the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param collection The collection to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns the composed aggregate object.\n */\n groupBy(\n collection: List | null | undefined,\n iteratee?: ListIterator\n ): Dictionary;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n collection: List | null | undefined,\n iteratee?: ListIterator\n ): Dictionary;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n collection: List|Dictionary | null | undefined,\n iteratee?: string\n ): Dictionary;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n collection: List|Dictionary | null | undefined,\n iteratee?: string\n ): Dictionary;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n collection: List|Dictionary | null | undefined,\n iteratee?: TWhere\n ): Dictionary;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n collection: List|Dictionary | null | undefined,\n iteratee?: Object\n ): Dictionary;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: TWhere\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: TWhere\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: Object\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: TWhere\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: TWhere\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.groupBy\n */\n groupBy(\n iteratee?: Object\n ): LoDashExplicitObjectWrapper>;\n }\n\n //_.includes\n interface LoDashStatic {\n /**\n * Checks if target is in collection using SameValueZero for equality comparisons. If fromIndex is negative,\n * it’s used as the offset from the end of collection.\n *\n * @param collection The collection to search.\n * @param target The value to search for.\n * @param fromIndex The index to search from.\n * @return True if the target element is found, else false.\n */\n includes(\n collection: List|Dictionary | null | undefined,\n target: T,\n fromIndex?: number\n ): boolean;\n\n /**\n * @see _.includes\n */\n includes(\n collection: string | null | undefined,\n target: string,\n fromIndex?: number\n ): boolean;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.includes\n */\n includes(\n target: T,\n fromIndex?: number\n ): boolean;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.includes\n */\n includes(\n target: TValue,\n fromIndex?: number\n ): boolean;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.includes\n */\n includes(\n target: string,\n fromIndex?: number\n ): boolean;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.includes\n */\n includes(\n target: T,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.includes\n */\n includes(\n target: TValue,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.includes\n */\n includes(\n target: string,\n fromIndex?: number\n ): LoDashExplicitWrapper;\n }\n\n //_.keyBy\n interface LoDashStatic {\n /**\n * Creates an object composed of keys generated from the results of running each element of collection through\n * iteratee. The corresponding value of each key is the last element responsible for generating the key. The\n * iteratee function is bound to thisArg and invoked with three arguments:\n * (value, index|key, collection).\n *\n * If a property name is provided for iteratee the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for iteratee the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param collection The collection to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns the composed aggregate object.\n */\n keyBy(\n collection: List | null | undefined,\n iteratee?: ListIterator\n ): Dictionary;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n collection: NumericDictionary | null | undefined,\n iteratee?: NumericDictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n collection: List|NumericDictionary|Dictionary | null | undefined,\n iteratee?: string\n ): Dictionary;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n collection: List|NumericDictionary|Dictionary | null | undefined,\n iteratee?: W\n ): Dictionary;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n collection: List|NumericDictionary|Dictionary | null | undefined,\n iteratee?: Object\n ): Dictionary;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: ListIterator\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: ListIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: W\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: ListIterator|NumericDictionaryIterator|DictionaryIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: W\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: Object\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: ListIterator\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: ListIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: W\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: ListIterator|NumericDictionaryIterator|DictionaryIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: W\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.keyBy\n */\n keyBy(\n iteratee?: Object\n ): LoDashExplicitObjectWrapper>;\n }\n\n //_.invoke\n interface LoDashStatic {\n /**\n * Invokes the method at path of object.\n * @param object The object to query.\n * @param path The path of the method to invoke.\n * @param args The arguments to invoke the method with.\n **/\n invoke(\n object: TObject,\n path: Many,\n ...args: any[]): TResult;\n\n /**\n * @see _.invoke\n **/\n invoke(\n object: Dictionary|TValue[],\n path: Many,\n ...args: any[]): TResult;\n\n /**\n * @see _.invoke\n **/\n invoke(\n object: any,\n path: Many,\n ...args: any[]): TResult;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.invoke\n **/\n invoke(\n path: Many,\n ...args: any[]): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.invoke\n **/\n invoke(\n path: Many,\n ...args: any[]): TResult;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.invoke\n **/\n invoke(\n path: Many,\n ...args: any[]): TResult;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.invoke\n **/\n invoke(\n path: Many,\n ...args: any[]): TResult;\n }\n\n //_.invokeMap\n interface LoDashStatic {\n /**\n * Invokes the method named by methodName on each element in the collection returning\n * an array of the results of each invoked method. Additional arguments will be provided\n * to each invoked method. If methodName is a function it will be invoked for, and this\n * bound to, each element in the collection.\n * @param collection The collection to iterate over.\n * @param methodName The name of the method to invoke.\n * @param args Arguments to invoke the method with.\n **/\n invokeMap(\n collection: TValue[] | null | undefined,\n methodName: string,\n ...args: any[]): TResult[];\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n collection: Dictionary | null | undefined,\n methodName: string,\n ...args: any[]): TResult[];\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n collection: Array<{}> | null | undefined,\n methodName: string,\n ...args: any[]): TResult[];\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n collection: Dictionary<{}> | null | undefined,\n methodName: string,\n ...args: any[]): TResult[];\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n collection: TValue[] | null | undefined,\n method: (...args: any[]) => TResult,\n ...args: any[]): TResult[];\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n collection: Dictionary | null | undefined,\n method: (...args: any[]) => TResult,\n ...args: any[]): TResult[];\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n collection: Array<{}> | null | undefined,\n method: (...args: any[]) => TResult,\n ...args: any[]): TResult[];\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n collection: Dictionary<{}> | null | undefined,\n method: (...args: any[]) => TResult,\n ...args: any[]): TResult[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n methodName: string,\n ...args: any[]): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n method: (...args: any[]) => TResult,\n ...args: any[]): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n methodName: string,\n ...args: any[]): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n method: (...args: any[]) => TResult,\n ...args: any[]): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n methodName: string,\n ...args: any[]): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n method: (...args: any[]) => TResult,\n ...args: any[]): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n methodName: string,\n ...args: any[]): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.invokeMap\n **/\n invokeMap(\n method: (...args: any[]) => TResult,\n ...args: any[]): LoDashExplicitArrayWrapper;\n }\n\n //_.map\n interface LoDashStatic {\n /**\n * Creates an array of values by running each element in collection through iteratee. The iteratee is bound to\n * thisArg and invoked with three arguments: (value, index|key, collection).\n *\n * If a property name is provided for iteratee the created _.property style callback returns the property value\n * of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for iteratee the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * Many lodash methods are guarded to work as iteratees for methods like _.every, _.filter, _.map, _.mapValues,\n * _.reject, and _.some.\n *\n * The guarded methods are:\n * ary, callback, chunk, clone, create, curry, curryRight, drop, dropRight, every, fill, flatten, invert, max,\n * min, parseInt, slice, sortBy, take, takeRight, template, trim, trimLeft, trimRight, trunc, random, range,\n * sample, some, sum, uniq, and words\n *\n * @param collection The collection to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns the new mapped array.\n */\n map(\n collection: List | null | undefined,\n iteratee: ListIterator\n ): TResult[];\n\n /**\n * @see _.map\n */\n map(collection: List | null | undefined): T[];\n\n /**\n * @see _.map\n */\n map(\n collection: Dictionary | null | undefined,\n iteratee: DictionaryIterator\n ): TResult[];\n\n /** @see _.map */\n map(\n collection: Dictionary | null | undefined,\n iteratee: K\n ): T[K][];\n\n /** @see _.map */\n map(collection: Dictionary | null | undefined): T[];\n\n map(\n collection: NumericDictionary | null | undefined,\n iteratee?: NumericDictionaryIterator\n ): TResult[];\n\n /** @see _.map */\n map(collection: List | null | undefined, iteratee: K): T[K][];\n\n /**\n * @see _.map\n */\n map(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n iteratee?: string\n ): TResult[];\n\n /**\n * @see _.map\n */\n map(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n iteratee?: TObject\n ): boolean[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.map\n */\n map(\n iteratee: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(): LoDashImplicitArrayWrapper;\n\n /** @see _.map */\n map(iteratee: K): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: TObject\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.map\n */\n map(\n iteratee: ListIterator|DictionaryIterator\n ): LoDashImplicitArrayWrapper;\n\n /** @see _.map */\n map(): LoDashImplicitArrayWrapper;\n\n /** @see _.map */\n map(iteratee: K): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: TObject\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.map\n */\n map(\n iteratee: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /** @see _.map */\n map(): LoDashExplicitArrayWrapper;\n\n /** @see _.map */\n map(iteratee: K): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: TObject\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.map\n */\n map(\n iteratee: ListIterator|DictionaryIterator\n ): LoDashExplicitArrayWrapper;\n\n /** @see _.map */\n map(): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.map\n */\n map(\n iteratee: TObject\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.partition\n interface LoDashStatic {\n /**\n * Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for,\n * while the second of which contains elements predicate returns falsey for.\n * The predicate is bound to thisArg and invoked with three arguments: (value, index|key, collection).\n *\n * If a property name is provided for predicate the created _.property style callback\n * returns the property value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback\n * returns true for elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns\n * true for elements that have the properties of the given object, else false.\n *\n * @param collection The collection to iterate over.\n * @param callback The function called per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the array of grouped elements.\n **/\n partition(\n collection: List | null | undefined,\n callback: ListIterator): T[][];\n\n /**\n * @see _.partition\n **/\n partition(\n collection: Dictionary | null | undefined,\n callback: DictionaryIterator): T[][];\n\n /**\n * @see _.partition\n **/\n partition(\n collection: List | null | undefined,\n whereValue: W): T[][];\n\n /**\n * @see _.partition\n **/\n partition(\n collection: Dictionary | null | undefined,\n whereValue: W): T[][];\n\n /**\n * @see _.partition\n **/\n partition(\n collection: List | null | undefined,\n path: string,\n srcValue: any): T[][];\n\n /**\n * @see _.partition\n **/\n partition(\n collection: Dictionary | null | undefined,\n path: string,\n srcValue: any): T[][];\n\n /**\n * @see _.partition\n **/\n partition(\n collection: List | null | undefined,\n pluckValue: string): T[][];\n\n /**\n * @see _.partition\n **/\n partition(\n collection: Dictionary | null | undefined,\n pluckValue: string): T[][];\n }\n\n interface LoDashImplicitStringWrapper {\n /**\n * @see _.partition\n */\n partition(\n callback: ListIterator): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.partition\n */\n partition(\n callback: ListIterator): LoDashImplicitArrayWrapper;\n /**\n * @see _.partition\n */\n partition(\n whereValue: W): LoDashImplicitArrayWrapper;\n /**\n * @see _.partition\n */\n partition(\n path: string,\n srcValue: any): LoDashImplicitArrayWrapper;\n /**\n * @see _.partition\n */\n partition(\n pluckValue: string): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.partition\n */\n partition(\n callback: ListIterator): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.partition\n */\n partition(\n callback: DictionaryIterator): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.partition\n */\n partition(\n whereValue: W): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.partition\n */\n partition(\n path: string,\n srcValue: any): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.partition\n */\n partition(\n pluckValue: string): LoDashImplicitArrayWrapper;\n }\n\n //_.reduce\n interface LoDashStatic {\n /**\n * Reduces a collection to a value which is the accumulated result of running each\n * element in the collection through the callback, where each successive callback execution\n * consumes the return value of the previous execution. If accumulator is not provided the\n * first element of the collection will be used as the initial accumulator value. The callback\n * is bound to thisArg and invoked with four arguments; (accumulator, value, index|key, collection).\n * @param collection The collection to iterate over.\n * @param callback The function called per iteration.\n * @param accumulator Initial value of the accumulator.\n * @param thisArg The this binding of callback.\n * @return Returns the accumulated value.\n **/\n reduce(\n collection: T[] | null | undefined,\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduce\n **/\n reduce(\n collection: List | null | undefined,\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduce\n **/\n reduce(\n collection: Dictionary | null | undefined,\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduce\n **/\n reduce(\n collection: NumericDictionary | null | undefined,\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduce\n **/\n reduce(\n collection: T[] | null | undefined,\n callback: MemoIterator): TResult | undefined;\n\n /**\n * @see _.reduce\n **/\n reduce(\n collection: List | null | undefined,\n callback: MemoIterator): TResult | undefined;\n\n /**\n * @see _.reduce\n **/\n reduce(\n collection: Dictionary | null | undefined,\n callback: MemoIterator): TResult | undefined;\n\n /**\n * @see _.reduce\n **/\n reduce(\n collection: NumericDictionary | null | undefined,\n callback: MemoIterator): TResult | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.reduce\n **/\n reduce(\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduce\n **/\n reduce(\n callback: MemoIterator): TResult | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.reduce\n **/\n reduce(\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduce\n **/\n reduce(\n callback: MemoIterator): TResult | undefined;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.reduce\n **/\n reduce(\n callback: MemoIterator,\n accumulator: TResult): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.reduce\n **/\n reduce(\n callback: MemoIterator): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**LoDashExplicitWrapper\n * @see _.reduce\n */\n reduce(\n callback: MemoIterator,\n accumulator: TResult): LoDashExplicitWrapper;\n\n /**\n * @see _.reduce\n */\n reduce(\n callback: MemoIterator): LoDashExplicitWrapper;\n }\n\n //_.reduceRight\n interface LoDashStatic {\n /**\n * This method is like _.reduce except that it iterates over elements of a collection from\n * right to left.\n * @param collection The collection to iterate over.\n * @param callback The function called per iteration.\n * @param accumulator Initial value of the accumulator.\n * @param thisArg The this binding of callback.\n * @return The accumulated value.\n **/\n reduceRight(\n collection: T[] | null | undefined,\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduceRight\n **/\n reduceRight(\n collection: List | null | undefined,\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduceRight\n **/\n reduceRight(\n collection: Dictionary | null | undefined,\n callback: MemoIterator,\n accumulator: TResult): TResult;\n\n /**\n * @see _.reduceRight\n **/\n reduceRight(\n collection: T[] | null | undefined,\n callback: MemoIterator): TResult | undefined;\n\n /**\n * @see _.reduceRight\n **/\n reduceRight(\n collection: List | null | undefined,\n callback: MemoIterator): TResult | undefined;\n\n /**\n * @see _.reduceRight\n **/\n reduceRight(\n collection: Dictionary | null | undefined,\n callback: MemoIterator): TResult | undefined;\n }\n\n //_.reject\n interface LoDashStatic {\n /**\n * The opposite of _.filter; this method returns the elements of collection that predicate does not return\n * truthy for.\n *\n * @param collection The collection to iterate over.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the new filtered array.\n */\n reject(\n collection: List | null | undefined,\n predicate?: ListIterator\n ): T[];\n\n /**\n * @see _.reject\n */\n reject(\n collection: Dictionary | null | undefined,\n predicate?: DictionaryIterator\n ): T[];\n\n /**\n * @see _.reject\n */\n reject(\n collection: string | null | undefined,\n predicate?: StringIterator\n ): string[];\n\n /**\n * @see _.reject\n */\n reject(\n collection: List|Dictionary | null | undefined,\n predicate: string\n ): T[];\n\n /**\n * @see _.reject\n */\n reject(\n collection: List|Dictionary | null | undefined,\n predicate: W\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.reject\n */\n reject(\n predicate?: StringIterator\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.reject\n */\n reject(\n predicate: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(\n predicate: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(predicate: W): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.reject\n */\n reject(\n predicate: ListIterator|DictionaryIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(\n predicate: string\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(predicate: W): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.reject\n */\n reject(\n predicate?: StringIterator\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.reject\n */\n reject(\n predicate: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(\n predicate: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(predicate: W): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.reject\n */\n reject(\n predicate: ListIterator|DictionaryIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(\n predicate: string\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.reject\n */\n reject(predicate: W): LoDashExplicitArrayWrapper;\n }\n\n //_.sample\n interface LoDashStatic {\n /**\n * Gets a random element from collection.\n *\n * @param collection The collection to sample.\n * @return Returns the random element.\n */\n sample(\n collection: List | Dictionary | NumericDictionary | object | null | undefined\n ): T | undefined;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sample\n */\n sample(): string | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sample\n */\n sample(): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sample\n */\n sample(): T | undefined;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sample\n */\n sample(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sample\n */\n sample(): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sample\n */\n sample(): TWrapper;\n }\n\n //_.sampleSize\n interface LoDashStatic {\n /**\n * Gets n random elements at unique keys from collection up to the size of collection.\n *\n * @param collection The collection to sample.\n * @param n The number of elements to sample.\n * @return Returns the random elements.\n */\n sampleSize(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n n?: number\n ): T[];\n\n /**\n * @see _.sampleSize\n */\n sampleSize(\n collection: O | null | undefined,\n n?: number\n ): T[];\n\n /**\n * @see _.sampleSize\n */\n sampleSize(\n collection: Object | null | undefined,\n n?: number\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.sampleSize\n */\n sampleSize(\n n?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sampleSize\n */\n sampleSize(\n n?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sampleSize\n */\n sampleSize(\n n?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.sampleSize\n */\n sampleSize(\n n?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sampleSize\n */\n sampleSize(\n n?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sampleSize\n */\n sampleSize(\n n?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.shuffle\n interface LoDashStatic {\n /**\n * Creates an array of shuffled values, using a version of the Fisher-Yates shuffle.\n *\n * @param collection The collection to shuffle.\n * @return Returns the new shuffled array.\n */\n shuffle(collection: List|Dictionary | null | undefined): T[];\n\n /**\n * @see _.shuffle\n */\n shuffle(collection: string | null | undefined): string[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.shuffle\n */\n shuffle(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.shuffle\n */\n shuffle(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.shuffle\n */\n shuffle(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.shuffle\n */\n shuffle(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.shuffle\n */\n shuffle(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.shuffle\n */\n shuffle(): LoDashExplicitArrayWrapper;\n }\n\n //_.size\n interface LoDashStatic {\n /**\n * Gets the size of collection by returning its length for array-like values or the number of own enumerable\n * properties for objects.\n *\n * @param collection The collection to inspect.\n * @return Returns the size of collection.\n */\n size(collection: List|Dictionary | null | undefined): number;\n\n /**\n * @see _.size\n */\n size(collection: string | null | undefined): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.size\n */\n size(): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.size\n */\n size(): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.size\n */\n size(): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.size\n */\n size(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.size\n */\n size(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.size\n */\n size(): LoDashExplicitWrapper;\n }\n\n //_.some\n interface LoDashStatic {\n /**\n * Checks if predicate returns truthy for any element of collection. Iteration is stopped once predicate\n * returns truthy. The predicate is invoked with three arguments: (value, index|key, collection).\n *\n * @param collection The collection to iterate over.\n * @param predicate The function invoked per iteration.\n * @return Returns true if any element passes the predicate check, else false.\n */\n some(\n collection: List | null | undefined,\n predicate?: ListIterator\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: Dictionary | null | undefined,\n predicate?: DictionaryIterator\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: NumericDictionary | null | undefined,\n predicate?: NumericDictionaryIterator\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: Object | null | undefined,\n predicate?: ObjectIterator\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n predicate?: string|[string, any]\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: Object | null | undefined,\n predicate?: string|[string, any]\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n predicate?: PartialObject\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: List|Dictionary|NumericDictionary | null | undefined,\n predicate?: PartialObject\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n collection: Object | null | undefined,\n predicate?: PartialObject\n ): boolean;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.some\n */\n some(\n predicate?: ListIterator|NumericDictionaryIterator\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n predicate?: string|[string, any]\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n predicate?: PartialObject\n ): boolean;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.some\n */\n some(\n predicate?: ListIterator|DictionaryIterator|NumericDictionaryIterator|ObjectIterator\n ): boolean;\n /**\n * @see _.some\n */\n some(\n predicate?: string|[string, any]\n ): boolean;\n\n /**\n * @see _.some\n */\n some(\n predicate?: PartialObject\n ): boolean;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.some\n */\n some(\n predicate?: ListIterator|NumericDictionaryIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.some\n */\n some(\n predicate?: string|[string, any]\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.some\n */\n some(\n predicate?: PartialObject\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.some\n */\n some(\n predicate?: ListIterator|DictionaryIterator|NumericDictionaryIterator|ObjectIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.some\n */\n some(\n predicate?: string|[string, any]\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.some\n */\n some(\n predicate?: PartialObject\n ): LoDashExplicitWrapper;\n }\n\n //_.sortBy\n interface LoDashStatic {\n /**\n * Creates an array of elements, sorted in ascending order by the results of\n * running each element in a collection through each iteratee. This method\n * performs a stable sort, that is, it preserves the original sort order of\n * equal elements. The iteratees are invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {...(Function|Function[]|Object|Object[]|string|string[])} [iteratees=[_.identity]]\n * The iteratees to sort by, specified individually or in arrays.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 42 },\n * { 'user': 'barney', 'age': 34 }\n * ];\n *\n * _.sortBy(users, function(o) { return o.user; });\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]\n *\n * _.sortBy(users, ['user', 'age']);\n * // => objects for [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]]\n *\n * _.sortBy(users, 'user', function(o) {\n * return Math.floor(o.age / 10);\n * });\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]\n */\n sortBy(\n collection: List | null | undefined,\n iteratee?: ListIterator\n ): T[];\n\n /**\n * @see _.sortBy\n */\n sortBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): T[];\n\n /**\n * @see _.sortBy\n */\n sortBy(\n collection: List|Dictionary | null | undefined,\n iteratee: string\n ): T[];\n\n /**\n * @see _.sortBy\n */\n sortBy(\n collection: List|Dictionary | null | undefined,\n whereValue: W\n ): T[];\n\n /**\n * @see _.sortBy\n */\n sortBy(\n collection: List|Dictionary | null | undefined\n ): T[];\n\n /**\n * @see _.sortBy\n */\n sortBy(\n collection: List | null | undefined,\n iteratees: Array|string|Object>\n ): T[];\n\n /**\n * @see _.sortBy\n */\n sortBy(\n collection: List | null | undefined,\n ...iteratees: Array|Object|string>\n ): T[];\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sortBy\n */\n sortBy(\n iteratee?: ListIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(iteratee: string): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(whereValue: W): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(...iteratees: Array|Object|string>): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n **/\n sortBy(iteratees: Array|string|Object>): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sortBy\n */\n sortBy(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(iteratee: string): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(whereValue: W): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sortBy\n */\n sortBy(\n iteratee?: ListIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(iteratee: string): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(whereValue: W): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sortBy\n */\n sortBy(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(iteratee: string): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(whereValue: W): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.sortBy\n */\n sortBy(): LoDashExplicitArrayWrapper;\n }\n\n //_.orderBy\n interface LoDashStatic {\n /**\n * This method is like `_.sortBy` except that it allows specifying the sort\n * orders of the iteratees to sort by. If `orders` is unspecified, all values\n * are sorted in ascending order. Otherwise, specify an order of \"desc\" for\n * descending or \"asc\" for ascending sort order of corresponding values.\n *\n * @static\n * @memberOf _\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function[]|Object[]|string[]} [iteratees=[_.identity]] The iteratees to sort by.\n * @param {string[]} [orders] The sort orders of `iteratees`.\n * @param- {Object} [guard] Enables use as an iteratee for functions like `_.reduce`.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 34 },\n * { 'user': 'fred', 'age': 42 },\n * { 'user': 'barney', 'age': 36 }\n * ];\n *\n * // sort by `user` in ascending order and by `age` in descending order\n * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]\n */\n orderBy(\n collection: List | null | undefined,\n iteratees?: Many|string|W>,\n orders?: Many\n ): T[];\n\n /**\n * @see _.orderBy\n */\n orderBy(\n collection: List | null | undefined,\n iteratees?: Many|string|Object>,\n orders?: Many\n ): T[];\n\n /**\n * @see _.orderBy\n */\n orderBy(\n collection: NumericDictionary | null | undefined,\n iteratees?: Many|string|W>,\n orders?: Many\n ): T[];\n\n /**\n * @see _.orderBy\n */\n orderBy(\n collection: NumericDictionary | null | undefined,\n iteratees?: Many|string|Object>,\n orders?: Many\n ): T[];\n\n /**\n * @see _.orderBy\n */\n orderBy(\n collection: Dictionary | null | undefined,\n iteratees?: Many|string|W>,\n orders?: Many\n ): T[];\n\n /**\n * @see _.orderBy\n */\n orderBy(\n collection: Dictionary | null | undefined,\n iteratees?: Many|string|Object>,\n orders?: Many\n ): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|Object>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|Object>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|Object>,\n orders?: Many\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W|(ListIterator|string|W)>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|Object>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|Object>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|W>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.orderBy\n */\n orderBy(\n iteratees?: Many|string|Object>,\n orders?: Many\n ): LoDashExplicitArrayWrapper;\n }\n\n /********\n * Date *\n ********/\n\n //_.now\n interface LoDashStatic {\n /**\n * Gets the number of milliseconds that have elapsed since the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @return The number of milliseconds.\n */\n now(): number;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.now\n */\n now(): number;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.now\n */\n now(): LoDashExplicitWrapper;\n }\n\n /*************\n * Functions *\n *************/\n\n //_.after\n interface LoDashStatic {\n /**\n * The opposite of _.before; this method creates a function that invokes func once it’s called n or more times.\n *\n * @param n The number of calls before func is invoked.\n * @param func The function to restrict.\n * @return Returns the new restricted function.\n */\n after(\n n: number,\n func: TFunc\n ): TFunc;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.after\n **/\n after(func: TFunc): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.after\n **/\n after(func: TFunc): LoDashExplicitObjectWrapper;\n }\n\n //_.ary\n interface LoDashStatic {\n /**\n * Creates a function that accepts up to n arguments ignoring any additional arguments.\n *\n * @param func The function to cap arguments for.\n * @param n The arity cap.\n * @returns Returns the new function.\n */\n ary(\n func: Function,\n n?: number\n ): TResult;\n\n ary(\n func: T,\n n?: number\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.ary\n */\n ary(n?: number): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.ary\n */\n ary(n?: number): LoDashExplicitObjectWrapper;\n }\n\n //_.before\n interface LoDashStatic {\n /**\n * Creates a function that invokes func, with the this binding and arguments of the created function, while\n * it’s called less than n times. Subsequent calls to the created function return the result of the last func\n * invocation.\n *\n * @param n The number of calls at which func is no longer invoked.\n * @param func The function to restrict.\n * @return Returns the new restricted function.\n */\n before(\n n: number,\n func: TFunc\n ): TFunc;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.before\n **/\n before(func: TFunc): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.before\n **/\n before(func: TFunc): LoDashExplicitObjectWrapper;\n }\n\n //_.bind\n interface FunctionBind {\n placeholder: any;\n\n (\n func: T,\n thisArg: any,\n ...partials: any[]\n ): TResult;\n\n (\n func: Function,\n thisArg: any,\n ...partials: any[]\n ): TResult;\n }\n\n interface LoDashStatic {\n /**\n * Creates a function that invokes func with the this binding of thisArg and prepends any additional _.bind\n * arguments to those provided to the bound function.\n *\n * The _.bind.placeholder value, which defaults to _ in monolithic builds, may be used as a placeholder for\n * partially applied arguments.\n *\n * Note: Unlike native Function#bind this method does not set the \"length\" property of bound functions.\n *\n * @param func The function to bind.\n * @param thisArg The this binding of func.\n * @param partials The arguments to be partially applied.\n * @return Returns the new bound function.\n */\n bind: FunctionBind;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.bind\n */\n bind(\n thisArg: any,\n ...partials: any[]\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.bind\n */\n bind(\n thisArg: any,\n ...partials: any[]\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.bindAll\n interface LoDashStatic {\n /**\n * Binds methods of an object to the object itself, overwriting the existing method. Method names may be\n * specified as individual arguments or as arrays of method names. If no method names are provided all\n * enumerable function properties, own and inherited, of object are bound.\n *\n * Note: This method does not set the \"length\" property of bound functions.\n *\n * @param object The object to bind and assign the bound methods to.\n * @param methodNames The object method names to bind, specified as individual method names or arrays of\n * method names.\n * @return Returns object.\n */\n bindAll(\n object: T,\n ...methodNames: Array>\n ): T;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.bindAll\n */\n bindAll(...methodNames: Array>): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.bindAll\n */\n bindAll(...methodNames: Array>): LoDashExplicitObjectWrapper;\n }\n\n //_.bindKey\n interface FunctionBindKey {\n placeholder: any;\n\n (\n object: T,\n key: any,\n ...partials: any[]\n ): TResult;\n\n (\n object: Object,\n key: any,\n ...partials: any[]\n ): TResult;\n }\n\n interface LoDashStatic {\n /**\n * Creates a function that invokes the method at object[key] and prepends any additional _.bindKey arguments\n * to those provided to the bound function.\n *\n * This method differs from _.bind by allowing bound functions to reference methods that may be redefined\n * or don’t yet exist. See Peter Michaux’s article for more details.\n *\n * The _.bindKey.placeholder value, which defaults to _ in monolithic builds, may be used as a placeholder\n * for partially applied arguments.\n *\n * @param object The object the method belongs to.\n * @param key The key of the method.\n * @param partials The arguments to be partially applied.\n * @return Returns the new bound function.\n */\n bindKey: FunctionBindKey;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.bindKey\n */\n bindKey(\n key: any,\n ...partials: any[]\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.bindKey\n */\n bindKey(\n key: any,\n ...partials: any[]\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.createCallback\n interface LoDashStatic {\n /**\n * Produces a callback bound to an optional thisArg. If func is a property name the created\n * callback will return the property value for a given element. If func is an object the created\n * callback will return true for elements that contain the equivalent object properties,\n * otherwise it will return false.\n * @param func The value to convert to a callback.\n * @param thisArg The this binding of the created callback.\n * @param argCount The number of arguments the callback accepts.\n * @return A callback function.\n **/\n createCallback(\n func: string,\n argCount?: number): () => any;\n\n /**\n * @see _.createCallback\n **/\n createCallback(\n func: Dictionary,\n argCount?: number): () => boolean;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.createCallback\n **/\n createCallback(\n argCount?: number): LoDashImplicitObjectWrapper<() => any>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.createCallback\n **/\n createCallback(\n argCount?: number): LoDashImplicitObjectWrapper<() => any>;\n }\n\n //_.curry\n interface LoDashStatic {\n /**\n * Creates a function that accepts one or more arguments of func that when called either invokes func returning\n * its result, if all func arguments have been provided, or returns a function that accepts one or more of the\n * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curry(func: (t1: T1) => R):\n CurriedFunction1;\n /**\n * Creates a function that accepts one or more arguments of func that when called either invokes func returning\n * its result, if all func arguments have been provided, or returns a function that accepts one or more of the\n * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curry(func: (t1: T1, t2: T2) => R):\n CurriedFunction2;\n /**\n * Creates a function that accepts one or more arguments of func that when called either invokes func returning\n * its result, if all func arguments have been provided, or returns a function that accepts one or more of the\n * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curry(func: (t1: T1, t2: T2, t3: T3) => R):\n CurriedFunction3;\n /**\n * Creates a function that accepts one or more arguments of func that when called either invokes func returning\n * its result, if all func arguments have been provided, or returns a function that accepts one or more of the\n * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curry(func: (t1: T1, t2: T2, t3: T3, t4: T4) => R):\n CurriedFunction4;\n /**\n * Creates a function that accepts one or more arguments of func that when called either invokes func returning\n * its result, if all func arguments have been provided, or returns a function that accepts one or more of the\n * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curry(func: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R):\n CurriedFunction5;\n /**\n * Creates a function that accepts one or more arguments of func that when called either invokes func returning\n * its result, if all func arguments have been provided, or returns a function that accepts one or more of the\n * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.\n * @param func The function to curry.\n * @param arity The arity of func.\n * @return Returns the new curried function.\n */\n curry(\n func: Function,\n arity?: number): TResult;\n }\n\n interface CurriedFunction1 {\n (): CurriedFunction1;\n (t1: T1): R;\n }\n\n interface CurriedFunction2 {\n (): CurriedFunction2;\n (t1: T1): CurriedFunction1;\n (t1: T1, t2: T2): R;\n }\n\n interface CurriedFunction3 {\n (): CurriedFunction3;\n (t1: T1): CurriedFunction2;\n (t1: T1, t2: T2): CurriedFunction1;\n (t1: T1, t2: T2, t3: T3): R;\n }\n\n interface CurriedFunction4 {\n (): CurriedFunction4;\n (t1: T1): CurriedFunction3;\n (t1: T1, t2: T2): CurriedFunction2;\n (t1: T1, t2: T2, t3: T3): CurriedFunction1;\n (t1: T1, t2: T2, t3: T3, t4: T4): R;\n }\n\n interface CurriedFunction5 {\n (): CurriedFunction5;\n (t1: T1): CurriedFunction4;\n (t1: T1, t2: T2): CurriedFunction3;\n (t1: T1, t2: T2, t3: T3): CurriedFunction2;\n (t1: T1, t2: T2, t3: T3, t4: T4): CurriedFunction1;\n (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5): R;\n }\n interface RightCurriedFunction1{\n ():RightCurriedFunction1\n (t1:T1):R\n }\n interface RightCurriedFunction2{\n ():RightCurriedFunction2\n (t2:T2):RightCurriedFunction1\n (t1:T1,t2:T2):R\n }\n interface RightCurriedFunction3{\n ():RightCurriedFunction3\n (t3:T3):RightCurriedFunction2\n (t2:T2,t3:T3):RightCurriedFunction1\n (t1:T1,t2:T2,t3:T3):R\n }\n interface RightCurriedFunction4{\n ():RightCurriedFunction4\n (t4:T4):RightCurriedFunction3\n (t3:T3,t4:T4):RightCurriedFunction2\n (t2:T2,t3:T3,t4:T4):RightCurriedFunction1\n (t1:T1,t2:T2,t3:T3,t4:T4):R\n }\n interface RightCurriedFunction5{\n ():RightCurriedFunction5\n (t5:T5):RightCurriedFunction4\n (t4:T4,t5:T5):RightCurriedFunction3\n (t3:T3,t4:T4,t5:T5):RightCurriedFunction2\n (t2:T2,t3:T3,t4:T4,t5:T5):RightCurriedFunction1\n (t1:T1,t2:T2,t3:T3,t4:T4,t5:T5):R\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.curry\n **/\n curry(arity?: number): LoDashImplicitObjectWrapper;\n }\n\n //_.curryRight\n interface LoDashStatic {\n /**\n * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight\n * instead of _.partial.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curryRight(func: (t1: T1) => R):\n RightCurriedFunction1;\n /**\n * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight\n * instead of _.partial.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curryRight(func: (t1: T1, t2: T2) => R):\n RightCurriedFunction2;\n /**\n * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight\n * instead of _.partial.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curryRight(func: (t1: T1, t2: T2, t3: T3) => R):\n RightCurriedFunction3;\n /**\n * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight\n * instead of _.partial.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curryRight(func: (t1: T1, t2: T2, t3: T3, t4: T4) => R):\n RightCurriedFunction4;\n /**\n * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight\n * instead of _.partial.\n * @param func The function to curry.\n * @return Returns the new curried function.\n */\n curryRight(func: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R):\n RightCurriedFunction5;\n /**\n * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight\n * instead of _.partial.\n * @param func The function to curry.\n * @param arity The arity of func.\n * @return Returns the new curried function.\n */\n curryRight(\n func: Function,\n arity?: number): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.curryRight\n **/\n curryRight(arity?: number): LoDashImplicitObjectWrapper;\n }\n\n //_.debounce\n interface DebounceSettings {\n /**\n * Specify invoking on the leading edge of the timeout.\n */\n leading?: boolean;\n\n /**\n * The maximum time func is allowed to be delayed before it’s invoked.\n */\n maxWait?: number;\n\n /**\n * Specify invoking on the trailing edge of the timeout.\n */\n trailing?: boolean;\n }\n\n interface LoDashStatic {\n /**\n * Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since\n * the last time the debounced function was invoked. The debounced function comes with a cancel method to\n * cancel delayed invocations and a flush method to immediately invoke them. Provide an options object to\n * indicate that func should be invoked on the leading and/or trailing edge of the wait timeout. Subsequent\n * calls to the debounced function return the result of the last func invocation.\n *\n * Note: If leading and trailing options are true, func is invoked on the trailing edge of the timeout only\n * if the the debounced function is invoked more than once during the wait timeout.\n *\n * See David Corbacho’s article for details over the differences between _.debounce and _.throttle.\n *\n * @param func The function to debounce.\n * @param wait The number of milliseconds to delay.\n * @param options The options object.\n * @param options.leading Specify invoking on the leading edge of the timeout.\n * @param options.maxWait The maximum time func is allowed to be delayed before it’s invoked.\n * @param options.trailing Specify invoking on the trailing edge of the timeout.\n * @return Returns the new debounced function.\n */\n debounce(\n func: T,\n wait?: number,\n options?: DebounceSettings\n ): T & Cancelable;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.debounce\n */\n debounce(\n wait?: number,\n options?: DebounceSettings\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.debounce\n */\n debounce(\n wait?: number,\n options?: DebounceSettings\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.defer\n interface LoDashStatic {\n /**\n * Defers invoking the func until the current call stack has cleared. Any additional arguments are provided to\n * func when it’s invoked.\n *\n * @param func The function to defer.\n * @param args The arguments to invoke the function with.\n * @return Returns the timer id.\n */\n defer(\n func: T,\n ...args: any[]\n ): number;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.defer\n */\n defer(...args: any[]): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.defer\n */\n defer(...args: any[]): LoDashExplicitWrapper;\n }\n\n //_.delay\n interface LoDashStatic {\n /**\n * Invokes func after wait milliseconds. Any additional arguments are provided to func when it’s invoked.\n *\n * @param func The function to delay.\n * @param wait The number of milliseconds to delay invocation.\n * @param args The arguments to invoke the function with.\n * @return Returns the timer id.\n */\n delay(\n func: T,\n wait: number,\n ...args: any[]\n ): number;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.delay\n */\n delay(\n wait: number,\n ...args: any[]\n ): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.delay\n */\n delay(\n wait: number,\n ...args: any[]\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashStatic {\n /**\n * Creates a function that invokes `func` with arguments reversed.\n *\n * @static\n * @memberOf _\n * @category Function\n * @param {Function} func The function to flip arguments for.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var flipped = _.flip(function() {\n * return _.toArray(arguments);\n * });\n *\n * flipped('a', 'b', 'c', 'd');\n * // => ['d', 'c', 'b', 'a']\n */\n flip(func: T): T;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.flip\n */\n flip(): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.flip\n */\n flip(): LoDashExplicitObjectWrapper;\n }\n\n //_.flow\n interface LoDashStatic {\n /**\n * Creates a function that returns the result of invoking the provided functions with the this binding of the\n * created function, where each successive invocation is supplied the return value of the previous.\n *\n * @param funcs Functions to invoke.\n * @return Returns the new function.\n */\n // 0-argument first function\n flow(f1: () => R1, f2: (a: R1) => R2): () => R2;\n flow(f1: () => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): () => R3;\n flow(f1: () => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): () => R4;\n flow(f1: () => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): () => R5;\n flow(f1: () => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): () => R6;\n flow(f1: () => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): () => R7;\n // 1-argument first function\n flow(f1: (a1: A1) => R1, f2: (a: R1) => R2): (a1: A1) => R2;\n flow(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1) => R3;\n flow(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1) => R4;\n flow(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1) => R5;\n flow(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1) => R6;\n flow(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1) => R7;\n // 2-argument first function\n flow(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2): (a1: A1, a2: A2) => R2;\n flow(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1, a2: A2) => R3;\n flow(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1, a2: A2) => R4;\n flow(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1, a2: A2) => R5;\n flow(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1, a2: A2) => R6;\n flow(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1, a2: A2) => R7;\n // 3-argument first function\n flow(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2): (a1: A1, a2: A2, a3: A3) => R2;\n flow(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1, a2: A2, a3: A3) => R3;\n flow(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1, a2: A2, a3: A3) => R4;\n flow(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1, a2: A2, a3: A3) => R5;\n flow(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1, a2: A2, a3: A3) => R6;\n flow(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1, a2: A2, a3: A3) => R7;\n // 4-argument first function\n flow(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2): (a1: A1, a2: A2, a3: A3, a4: A4) => R2;\n flow(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1, a2: A2, a3: A3, a4: A4) => R3;\n flow(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1, a2: A2, a3: A3, a4: A4) => R4;\n flow(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1, a2: A2, a3: A3, a4: A4) => R5;\n flow(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1, a2: A2, a3: A3, a4: A4) => R6;\n flow(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1, a2: A2, a3: A3, a4: A4) => R7;\n // generic function\n flow(...funcs: Function[]): TResult;\n flow(funcs: Function[]): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.flow\n */\n flow(...funcs: Function[]): LoDashImplicitObjectWrapper;\n /**\n * @see _.flow\n */\n flow(funcs: Function[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.flow\n */\n flow(...funcs: Function[]): LoDashExplicitObjectWrapper;\n /**\n * @see _.flow\n */\n flow(funcs: Function[]): LoDashExplicitObjectWrapper;\n }\n\n //_.flowRight\n interface LoDashStatic {\n /**\n * This method is like _.flow except that it creates a function that invokes the provided functions from right\n * to left.\n *\n * @param funcs Functions to invoke.\n * @return Returns the new function.\n */\n flowRight(...funcs: Function[]): TResult;\n /**\n * @see _.flowRight\n */\n flowRight(funcs: Function[]): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.flowRight\n */\n flowRight(...funcs: Function[]): LoDashImplicitObjectWrapper;\n /**\n * @see _.flowRight\n */\n flowRight(funcs: Function[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.flowRight\n */\n flowRight(...funcs: Function[]): LoDashExplicitObjectWrapper;\n /**\n * @see _.flowRight\n */\n flowRight(funcs: Function[]): LoDashExplicitObjectWrapper;\n }\n\n //_.memoize\n interface MemoizedFunction extends Function {\n cache: MapCache;\n }\n\n interface LoDashStatic {\n /**\n * Creates a function that memoizes the result of func. If resolver is provided it determines the cache key for\n * storing the result based on the arguments provided to the memoized function. By default, the first argument\n * provided to the memoized function is coerced to a string and used as the cache key. The func is invoked with\n * the this binding of the memoized function.\n *\n * @param func The function to have its output memoized.\n * @param resolver The function to resolve the cache key.\n * @return Returns the new memoizing function.\n */\n memoize: {\n (func: T, resolver?: Function): T & MemoizedFunction;\n Cache: MapCacheConstructor;\n };\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.memoize\n */\n memoize(resolver?: Function): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.memoize\n */\n memoize(resolver?: Function): LoDashExplicitObjectWrapper;\n }\n\n //_.overArgs (was _.modArgs)\n interface LoDashStatic {\n /**\n * Creates a function that runs each argument through a corresponding transform function.\n *\n * @param func The function to wrap.\n * @param transforms The functions to transform arguments, specified as individual functions or arrays\n * of functions.\n * @return Returns the new function.\n */\n overArgs(\n func: T,\n ...transforms: Function[]\n ): TResult;\n\n /**\n * @see _.overArgs\n */\n overArgs(\n func: T,\n transforms: Function[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.overArgs\n */\n overArgs(...transforms: Function[]): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.overArgs\n */\n overArgs(transforms: Function[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.overArgs\n */\n overArgs(...transforms: Function[]): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.overArgs\n */\n overArgs(transforms: Function[]): LoDashExplicitObjectWrapper;\n }\n\n //_.negate\n interface LoDashStatic {\n /**\n * Creates a function that negates the result of the predicate func. The func predicate is invoked with\n * the this binding and arguments of the created function.\n *\n * @param predicate The predicate to negate.\n * @return Returns the new function.\n */\n negate(predicate: T): (...args: any[]) => boolean;\n\n /**\n * @see _.negate\n */\n negate(predicate: T): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.negate\n */\n negate(): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;\n\n /**\n * @see _.negate\n */\n negate(): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.negate\n */\n negate(): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;\n\n /**\n * @see _.negate\n */\n negate(): LoDashExplicitObjectWrapper;\n }\n\n //_.once\n interface LoDashStatic {\n /**\n * Creates a function that is restricted to invoking func once. Repeat calls to the function return the value\n * of the first call. The func is invoked with the this binding and arguments of the created function.\n *\n * @param func The function to restrict.\n * @return Returns the new restricted function.\n */\n once(func: T): T;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.once\n */\n once(): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.once\n */\n once(): LoDashExplicitObjectWrapper;\n }\n\n //_.partial\n interface LoDashStatic {\n /**\n * Creates a function that, when called, invokes func with any additional partial arguments\n * prepended to those provided to the new function. This method is similar to _.bind except\n * it does not alter the this binding.\n * @param func The function to partially apply arguments to.\n * @param args Arguments to be partially applied.\n * @return The new partially applied function.\n **/\n partial: Partial;\n }\n\n type PH = LoDashStatic;\n\n type Function0 = () => R;\n type Function1 = (t1: T1) => R;\n type Function2 = (t1: T1, t2: T2) => R;\n type Function3 = (t1: T1, t2: T2, t3: T3) => R;\n type Function4 = (t1: T1, t2: T2, t3: T3, t4: T4) => R;\n\n interface Partial {\n // arity 0\n (func: Function0): Function0;\n // arity 1\n (func: Function1): Function1;\n (func: Function1, arg1: T1): Function0;\n // arity 2\n (func: Function2): Function2;\n (func: Function2, arg1: T1): Function1< T2, R>;\n (func: Function2, plc1: PH, arg2: T2): Function1;\n (func: Function2, arg1: T1, arg2: T2): Function0< R>;\n // arity 3\n (func: Function3): Function3;\n (func: Function3, arg1: T1): Function2< T2, T3, R>;\n (func: Function3, plc1: PH, arg2: T2): Function2;\n (func: Function3, arg1: T1, arg2: T2): Function1< T3, R>;\n (func: Function3, plc1: PH, plc2: PH, arg3: T3): Function2;\n (func: Function3, arg1: T1, plc2: PH, arg3: T3): Function1< T2, R>;\n (func: Function3, plc1: PH, arg2: T2, arg3: T3): Function1;\n (func: Function3, arg1: T1, arg2: T2, arg3: T3): Function0< R>;\n // arity 4\n (func: Function4): Function4;\n (func: Function4, arg1: T1): Function3< T2, T3, T4, R>;\n (func: Function4, plc1: PH, arg2: T2): Function3;\n (func: Function4, arg1: T1, arg2: T2): Function2< T3, T4, R>;\n (func: Function4, plc1: PH, plc2: PH, arg3: T3): Function3;\n (func: Function4, arg1: T1, plc2: PH, arg3: T3): Function2< T2, T4, R>;\n (func: Function4, plc1: PH, arg2: T2, arg3: T3): Function2;\n (func: Function4, arg1: T1, arg2: T2, arg3: T3): Function1< T4, R>;\n (func: Function4, plc1: PH, plc2: PH, plc3: PH, arg4: T4): Function3;\n (func: Function4, arg1: T1, plc2: PH, plc3: PH, arg4: T4): Function2< T2, T3, R>;\n (func: Function4, plc1: PH, arg2: T2, plc3: PH, arg4: T4): Function2;\n (func: Function4, arg1: T1, arg2: T2, plc3: PH, arg4: T4): Function1< T3, R>;\n (func: Function4, plc1: PH, plc2: PH, arg3: T3, arg4: T4): Function2;\n (func: Function4, arg1: T1, plc2: PH, arg3: T3, arg4: T4): Function1< T2, R>;\n (func: Function4, plc1: PH, arg2: T2, arg3: T3, arg4: T4): Function1;\n (func: Function4, arg1: T1, arg2: T2, arg3: T3, arg4: T4): Function0< R>;\n // catch-all\n (func: Function, ...args: any[]): Function;\n }\n\n //_.partialRight\n interface LoDashStatic {\n /**\n * This method is like _.partial except that partial arguments are appended to those provided\n * to the new function.\n * @param func The function to partially apply arguments to.\n * @param args Arguments to be partially applied.\n * @return The new partially applied function.\n **/\n partialRight: PartialRight;\n }\n\n interface PartialRight {\n // arity 0\n (func: Function0): Function0;\n // arity 1\n (func: Function1): Function1;\n (func: Function1, arg1: T1): Function0;\n // arity 2\n (func: Function2): Function2;\n (func: Function2, arg1: T1, plc2: PH): Function1< T2, R>;\n (func: Function2, arg2: T2): Function1;\n (func: Function2, arg1: T1, arg2: T2): Function0< R>;\n // arity 3\n (func: Function3): Function3;\n (func: Function3, arg1: T1, plc2: PH, plc3: PH): Function2< T2, T3, R>;\n (func: Function3, arg2: T2, plc3: PH): Function2;\n (func: Function3, arg1: T1, arg2: T2, plc3: PH): Function1< T3, R>;\n (func: Function3, arg3: T3): Function2;\n (func: Function3, arg1: T1, plc2: PH, arg3: T3): Function1< T2, R>;\n (func: Function3, arg2: T2, arg3: T3): Function1;\n (func: Function3, arg1: T1, arg2: T2, arg3: T3): Function0< R>;\n // arity 4\n (func: Function4): Function4;\n (func: Function4, arg1: T1, plc2: PH, plc3: PH, plc4: PH): Function3< T2, T3, T4, R>;\n (func: Function4, arg2: T2, plc3: PH, plc4: PH): Function3;\n (func: Function4, arg1: T1, arg2: T2, plc3: PH, plc4: PH): Function2< T3, T4, R>;\n (func: Function4, arg3: T3, plc4: PH): Function3;\n (func: Function4, arg1: T1, plc2: PH, arg3: T3, plc4: PH): Function2< T2, T4, R>;\n (func: Function4, arg2: T2, arg3: T3, plc4: PH): Function2;\n (func: Function4, arg1: T1, arg2: T2, arg3: T3, plc4: PH): Function1< T4, R>;\n (func: Function4, arg4: T4): Function3;\n (func: Function4, arg1: T1, plc2: PH, plc3: PH, arg4: T4): Function2< T2, T3, R>;\n (func: Function4, arg2: T2, plc3: PH, arg4: T4): Function2;\n (func: Function4, arg1: T1, arg2: T2, plc3: PH, arg4: T4): Function1< T3, R>;\n (func: Function4, arg3: T3, arg4: T4): Function2;\n (func: Function4, arg1: T1, plc2: PH, arg3: T3, arg4: T4): Function1< T2, R>;\n (func: Function4, arg2: T2, arg3: T3, arg4: T4): Function1;\n (func: Function4, arg1: T1, arg2: T2, arg3: T3, arg4: T4): Function0< R>;\n // catch-all\n (func: Function, ...args: any[]): Function;\n }\n\n //_.rearg\n interface LoDashStatic {\n /**\n * Creates a function that invokes func with arguments arranged according to the specified indexes where the\n * argument value at the first index is provided as the first argument, the argument value at the second index\n * is provided as the second argument, and so on.\n * @param func The function to rearrange arguments for.\n * @param indexes The arranged argument indexes, specified as individual indexes or arrays of indexes.\n * @return Returns the new function.\n */\n rearg(func: Function, indexes: number[]): TResult;\n\n /**\n * @see _.rearg\n */\n rearg(func: Function, ...indexes: number[]): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.rearg\n */\n rearg(indexes: number[]): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.rearg\n */\n rearg(...indexes: number[]): LoDashImplicitObjectWrapper;\n }\n\n //_.rest\n interface LoDashStatic {\n /**\n * Creates a function that invokes func with the this binding of the created function and arguments from start\n * and beyond provided as an array.\n *\n * Note: This method is based on the rest parameter.\n *\n * @param func The function to apply a rest parameter to.\n * @param start The start position of the rest parameter.\n * @return Returns the new function.\n */\n rest(\n func: Function,\n start?: number\n ): TResult;\n\n /**\n * @see _.rest\n */\n rest(\n func: TFunc,\n start?: number\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.rest\n */\n rest(start?: number): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.rest\n */\n rest(start?: number): LoDashExplicitObjectWrapper;\n }\n\n //_.spread\n interface LoDashStatic {\n /**\n * Creates a function that invokes func with the this binding of the created function and an array of arguments\n * much like Function#apply.\n *\n * Note: This method is based on the spread operator.\n *\n * @param func The function to spread arguments over.\n * @return Returns the new function.\n */\n spread(func: F): T;\n\n /**\n * @see _.spread\n */\n spread(func: Function): T;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.spread\n */\n spread(): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.spread\n */\n spread(): LoDashExplicitObjectWrapper;\n }\n\n //_.throttle\n interface ThrottleSettings {\n /**\n * If you'd like to disable the leading-edge call, pass this as false.\n */\n leading?: boolean;\n\n /**\n * If you'd like to disable the execution on the trailing-edge, pass false.\n */\n trailing?: boolean;\n }\n\n interface LoDashStatic {\n /**\n * Creates a throttled function that only invokes func at most once per every wait milliseconds. The throttled\n * function comes with a cancel method to cancel delayed invocations and a flush method to immediately invoke\n * them. Provide an options object to indicate that func should be invoked on the leading and/or trailing edge\n * of the wait timeout. Subsequent calls to the throttled function return the result of the last func call.\n *\n * Note: If leading and trailing options are true, func is invoked on the trailing edge of the timeout only if\n * the the throttled function is invoked more than once during the wait timeout.\n *\n * @param func The function to throttle.\n * @param wait The number of milliseconds to throttle invocations to.\n * @param options The options object.\n * @param options.leading Specify invoking on the leading edge of the timeout.\n * @param options.trailing Specify invoking on the trailing edge of the timeout.\n * @return Returns the new throttled function.\n */\n throttle(\n func: T,\n wait?: number,\n options?: ThrottleSettings\n ): T & Cancelable;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.throttle\n */\n throttle(\n wait?: number,\n options?: ThrottleSettings\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.throttle\n */\n throttle(\n wait?: number,\n options?: ThrottleSettings\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.unary\n interface LoDashStatic {\n /**\n * Creates a function that accepts up to one argument, ignoring any\n * additional arguments.\n *\n * @static\n * @memberOf _\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new function.\n * @example\n *\n * _.map(['6', '8', '10'], _.unary(parseInt));\n * // => [6, 8, 10]\n */\n unary(func: T): T;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.unary\n */\n unary(): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.unary\n */\n unary(): LoDashExplicitObjectWrapper;\n }\n\n //_.wrap\n interface LoDashStatic {\n /**\n * Creates a function that provides value to the wrapper function as its first argument. Any additional\n * arguments provided to the function are appended to those provided to the wrapper function. The wrapper is\n * invoked with the this binding of the created function.\n *\n * @param value The value to wrap.\n * @param wrapper The wrapper function.\n * @return Returns the new function.\n */\n wrap(\n value: V,\n wrapper: W\n ): R;\n\n /**\n * @see _.wrap\n */\n wrap(\n value: V,\n wrapper: Function\n ): R;\n\n /**\n * @see _.wrap\n */\n wrap(\n value: any,\n wrapper: Function\n ): R;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.wrap\n */\n wrap(wrapper: W): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.wrap\n */\n wrap(wrapper: Function): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.wrap\n */\n wrap(wrapper: W): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.wrap\n */\n wrap(wrapper: Function): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.wrap\n */\n wrap(wrapper: W): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.wrap\n */\n wrap(wrapper: Function): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.wrap\n */\n wrap(wrapper: W): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.wrap\n */\n wrap(wrapper: Function): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.wrap\n */\n wrap(wrapper: W): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.wrap\n */\n wrap(wrapper: Function): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.wrap\n */\n wrap(wrapper: W): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.wrap\n */\n wrap(wrapper: Function): LoDashExplicitObjectWrapper;\n }\n\n /********\n * Lang *\n ********/\n\n //_.castArray\n interface LoDashStatic {\n /**\n * Casts value as an array if it’s not one.\n *\n * @param value The value to inspect.\n * @return Returns the cast array.\n */\n castArray(value?: Many): T[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.castArray\n */\n castArray(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.castArray\n */\n castArray(): TWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.castArray\n */\n castArray(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.castArray\n */\n castArray(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.castArray\n */\n castArray(): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.castArray\n */\n castArray(): LoDashExplicitArrayWrapper;\n }\n\n //_.clone\n interface LoDashStatic {\n /**\n * Creates a shallow clone of value.\n *\n * Note: This method is loosely based on the structured clone algorithm and supports cloning arrays,\n * array buffers, booleans, date objects, maps, numbers, Object objects, regexes, sets, strings, symbols,\n * and typed arrays. The own enumerable properties of arguments objects are cloned as plain objects. An empty\n * object is returned for uncloneable values such as error objects, functions, DOM nodes, and WeakMaps.\n *\n * @param value The value to clone.\n * @return Returns the cloned value.\n */\n clone(value: T): T;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.clone\n */\n clone(): T;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.clone\n */\n clone(): TArray;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.clone\n */\n clone(): TObject;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.clone\n */\n clone(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.clone\n */\n clone(): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.clone\n */\n clone(): TWrapper;\n }\n\n //_.cloneDeep\n interface LoDashStatic {\n /**\n * This method is like _.clone except that it recursively clones value.\n *\n * @param value The value to recursively clone.\n * @return Returns the deep cloned value.\n */\n cloneDeep(value: T): T;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.cloneDeep\n */\n cloneDeep(): T;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.cloneDeep\n */\n cloneDeep(): TArray;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.cloneDeep\n */\n cloneDeep(): TObject;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.cloneDeep\n */\n cloneDeep(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.cloneDeep\n */\n cloneDeep(): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.cloneDeep\n */\n cloneDeep(): TWrapper;\n }\n\n //_.cloneDeepWith\n type CloneDeepWithCustomizer = (value: TValue, key?: number|string, object?: any, stack?: any) => TResult;\n\n interface LoDashStatic {\n /**\n * This method is like _.cloneWith except that it recursively clones value.\n *\n * @param value The value to recursively clone.\n * @param customizer The function to customize cloning.\n * @return Returns the deep cloned value.\n */\n cloneDeepWith(\n value: any,\n customizer?: CloneDeepWithCustomizer\n ): TResult;\n\n /**\n * @see _.clonDeepeWith\n */\n cloneDeepWith(\n value: T,\n customizer?: CloneDeepWithCustomizer\n ): TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): TResult;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): TResult;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.cloneDeepWith\n */\n cloneDeepWith(\n customizer?: CloneDeepWithCustomizer\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.cloneWith\n type CloneWithCustomizer = (value: TValue, key?: number|string, object?: any, stack?: any) => TResult;\n\n interface LoDashStatic {\n /**\n * This method is like _.clone except that it accepts customizer which is invoked to produce the cloned value.\n * If customizer returns undefined cloning is handled by the method instead.\n *\n * @param value The value to clone.\n * @param customizer The function to customize cloning.\n * @return Returns the cloned value.\n */\n cloneWith(\n value: any,\n customizer?: CloneWithCustomizer\n ): TResult;\n\n /**\n * @see _.cloneWith\n */\n cloneWith(\n value: T,\n customizer?: CloneWithCustomizer\n ): TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): TResult;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): TResult;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.cloneWith\n */\n cloneWith(\n customizer?: CloneWithCustomizer\n ): LoDashExplicitObjectWrapper;\n }\n\n /**\n * An object containing predicate functions for each property of T\n */\n type ConformsPredicateObject = {\n [P in keyof T]: (val: T[P]) => boolean;\n };\n\n //_.conforms\n interface LoDashStatic {\n /**\n * Creates a function that invokes the predicate properties of `source` with the corresponding\n * property values of a given object, returning true if all predicates return truthy, else false.\n */\n conforms(source: ConformsPredicateObject): (Target: T) => boolean;\n }\n\n //_.conformsTo\n interface LoDashStatic {\n /**\n * Checks if object conforms to source by invoking the predicate properties of source with the\n * corresponding property values of object.\n *\n * Note: This method is equivalent to _.conforms when source is partially applied.\n */\n conformsTo(object: T, source: ConformsPredicateObject): boolean;\n }\n\n //_.eq\n interface LoDashStatic {\n /**\n * Performs a [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'user': 'fred' };\n * var other = { 'user': 'fred' };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\n eq(\n value: any,\n other: any\n ): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isEqual\n */\n eq(\n other: any\n ): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isEqual\n */\n eq(\n other: any\n ): LoDashExplicitWrapper;\n }\n\n //_.gt\n interface LoDashStatic {\n /**\n * Checks if value is greater than other.\n *\n * @param value The value to compare.\n * @param other The other value to compare.\n * @return Returns true if value is greater than other, else false.\n */\n gt(\n value: any,\n other: any\n ): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.gt\n */\n gt(other: any): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.gt\n */\n gt(other: any): LoDashExplicitWrapper;\n }\n\n //_.gte\n interface LoDashStatic {\n /**\n * Checks if value is greater than or equal to other.\n *\n * @param value The value to compare.\n * @param other The other value to compare.\n * @return Returns true if value is greater than or equal to other, else false.\n */\n gte(\n value: any,\n other: any\n ): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.gte\n */\n gte(other: any): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.gte\n */\n gte(other: any): LoDashExplicitWrapper;\n }\n\n //_.isArguments\n interface LoDashStatic {\n /**\n * Checks if value is classified as an arguments object.\n *\n * @param value The value to check.\n * @return Returns true if value is correctly classified, else false.\n */\n isArguments(value?: any): value is IArguments;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isArguments\n */\n isArguments(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isArguments\n */\n isArguments(): LoDashExplicitWrapper;\n }\n\n //_.isArray\n interface LoDashStatic {\n /**\n * Checks if value is classified as an Array object.\n * @param value The value to check.\n *\n * @return Returns true if value is correctly classified, else false.\n */\n isArray(value?: any): value is any[];\n\n /**\n * DEPRECATED\n */\n isArray(value?: any): value is any[];\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isArray\n */\n isArray(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isArray\n */\n isArray(): LoDashExplicitWrapper;\n }\n\n //_.isArrayBuffer\n interface LoDashStatic {\n /**\n * Checks if value is classified as an ArrayBuffer object.\n *\n * @param value The value to check.\n * @return Returns true if value is correctly classified, else false.\n */\n isArrayBuffer(value?: any): value is ArrayBuffer;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isArrayBuffer\n */\n isArrayBuffer(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isArrayBuffer\n */\n isArrayBuffer(): LoDashExplicitWrapper;\n }\n\n //_.isArrayLike\n interface LoDashStatic {\n /**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @type Function\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\n isArrayLike(value: T & string & number): boolean; // should only match if T = any\n\n /**\n * @see _.isArrayLike\n */\n isArrayLike(value?: Function): value is never;\n\n /**\n * @see _.isArrayLike\n */\n isArrayLike(value: T | Function): value is T & { length: number };\n\n /**\n * DEPRECATED\n */\n isArrayLike(value?: any): value is any[];\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isArrayLike\n */\n isArrayLike(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isArrayLike\n */\n isArrayLike(): LoDashExplicitWrapper;\n }\n\n //_.isArrayLikeObject\n interface LoDashStatic {\n /**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @type Function\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object, else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\n isArrayLikeObject(value: T & string & number): boolean; // should only match if T = any\n\n /**\n * @see _.isArrayLike\n */\n isArrayLikeObject(value?: Function | string | boolean | number): value is never;\n\n /**\n * @see _.isArrayLike\n */\n isArrayLikeObject(value: T | Function | string | boolean | number): value is T & { length: number };\n\n /**\n * DEPRECATED\n */\n isArrayLikeObject(value?: any): value is any[];\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isArrayLikeObject\n */\n isArrayLikeObject(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isArrayLikeObject\n */\n isArrayLikeObject(): LoDashExplicitWrapper;\n }\n\n //_.isBoolean\n interface LoDashStatic {\n /**\n * Checks if value is classified as a boolean primitive or object.\n *\n * @param value The value to check.\n * @return Returns true if value is correctly classified, else false.\n */\n isBoolean(value?: any): value is boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isBoolean\n */\n isBoolean(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isBoolean\n */\n isBoolean(): LoDashExplicitWrapper;\n }\n\n //_.isBuffer\n interface LoDashStatic {\n /**\n * Checks if value is a buffer.\n *\n * @param value The value to check.\n * @return Returns true if value is a buffer, else false.\n */\n isBuffer(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isBuffer\n */\n isBuffer(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isBuffer\n */\n isBuffer(): LoDashExplicitWrapper;\n }\n\n //_.isDate\n interface LoDashStatic {\n /**\n * Checks if value is classified as a Date object.\n * @param value The value to check.\n *\n * @return Returns true if value is correctly classified, else false.\n */\n isDate(value?: any): value is Date;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isDate\n */\n isDate(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isDate\n */\n isDate(): LoDashExplicitWrapper;\n }\n\n //_.isElement\n interface LoDashStatic {\n /**\n * Checks if value is a DOM element.\n *\n * @param value The value to check.\n * @return Returns true if value is a DOM element, else false.\n */\n isElement(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isElement\n */\n isElement(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isElement\n */\n isElement(): LoDashExplicitWrapper;\n }\n\n //_.isEmpty\n interface LoDashStatic {\n /**\n * Checks if value is empty. A value is considered empty unless it’s an arguments object, array, string, or\n * jQuery-like collection with a length greater than 0 or an object with own enumerable properties.\n *\n * @param value The value to inspect.\n * @return Returns true if value is empty, else false.\n */\n isEmpty(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isEmpty\n */\n isEmpty(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isEmpty\n */\n isEmpty(): LoDashExplicitWrapper;\n }\n\n //_.isEqual\n interface LoDashStatic {\n /**\n * Performs a deep comparison between two values to determine if they are\n * equivalent.\n *\n * **Note:** This method supports comparing arrays, array buffers, booleans,\n * date objects, error objects, maps, numbers, `Object` objects, regexes,\n * sets, strings, symbols, and typed arrays. `Object` objects are compared\n * by their own, not inherited, enumerable properties. Functions and DOM\n * nodes are **not** supported.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'user': 'fred' };\n * var other = { 'user': 'fred' };\n *\n * _.isEqual(object, other);\n * // => true\n *\n * object === other;\n * // => false\n */\n isEqual(\n value: any,\n other: any\n ): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isEqual\n */\n isEqual(\n other: any\n ): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isEqual\n */\n isEqual(\n other: any\n ): LoDashExplicitWrapper;\n }\n\n // _.isEqualWith\n type IsEqualCustomizer = (value: any, other: any, indexOrKey: number|string|undefined, parent: any, otherParent: any, stack: any) => boolean|undefined;\n\n interface LoDashStatic {\n /**\n * This method is like `_.isEqual` except that it accepts `customizer` which is\n * invoked to compare values. If `customizer` returns `undefined` comparisons are\n * handled by the method instead. The `customizer` is invoked with up to seven arguments:\n * (objValue, othValue [, index|key, object, other, stack]).\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * function isGreeting(value) {\n * return /^h(?:i|ello)$/.test(value);\n * }\n *\n * function customizer(objValue, othValue) {\n * if (isGreeting(objValue) && isGreeting(othValue)) {\n * return true;\n * }\n * }\n *\n * var array = ['hello', 'goodbye'];\n * var other = ['hi', 'goodbye'];\n *\n * _.isEqualWith(array, other, customizer);\n * // => true\n */\n isEqualWith(\n value: any,\n other: any,\n customizer?: IsEqualCustomizer\n ): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isEqualWith\n */\n isEqualWith(\n other: any,\n customizer?: IsEqualCustomizer\n ): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isEqualWith\n */\n isEqualWith(\n other: any,\n customizer?: IsEqualCustomizer\n ): LoDashExplicitWrapper;\n }\n\n //_.isError\n interface LoDashStatic {\n /**\n * Checks if value is an Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, or URIError\n * object.\n *\n * @param value The value to check.\n * @return Returns true if value is an error object, else false.\n */\n isError(value: any): value is Error;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isError\n */\n isError(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isError\n */\n isError(): LoDashExplicitWrapper;\n }\n\n //_.isFinite\n interface LoDashStatic {\n /**\n * Checks if value is a finite primitive number.\n *\n * Note: This method is based on Number.isFinite.\n *\n * @param value The value to check.\n * @return Returns true if value is a finite number, else false.\n */\n isFinite(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isFinite\n */\n isFinite(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isFinite\n */\n isFinite(): LoDashExplicitWrapper;\n }\n\n //_.isFunction\n interface LoDashStatic {\n /**\n * Checks if value is classified as a Function object.\n *\n * @param value The value to check.\n * @return Returns true if value is correctly classified, else false.\n */\n isFunction(value?: any): value is Function;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isFunction\n */\n isFunction(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isFunction\n */\n isFunction(): LoDashExplicitWrapper;\n }\n\n //_.isInteger\n interface LoDashStatic {\n /**\n * Checks if `value` is an integer.\n *\n * **Note:** This method is based on [`Number.isInteger`](https://mdn.io/Number/isInteger).\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an integer, else `false`.\n * @example\n *\n * _.isInteger(3);\n * // => true\n *\n * _.isInteger(Number.MIN_VALUE);\n * // => false\n *\n * _.isInteger(Infinity);\n * // => false\n *\n * _.isInteger('3');\n * // => false\n */\n isInteger(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isInteger\n */\n isInteger(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isInteger\n */\n isInteger(): LoDashExplicitWrapper;\n }\n\n //_.isLength\n interface LoDashStatic {\n /**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This function is loosely based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\n isLength(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isLength\n */\n isLength(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isLength\n */\n isLength(): LoDashExplicitWrapper;\n }\n\n //_.isMap\n interface LoDashStatic {\n /**\n * Checks if value is classified as a Map object.\n *\n * @param value The value to check.\n * @returns Returns true if value is correctly classified, else false.\n */\n isMap(value?: any): value is Map;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isMap\n */\n isMap(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isMap\n */\n isMap(): LoDashExplicitWrapper;\n }\n\n //_.isMatch\n type isMatchCustomizer = (value: any, other: any, indexOrKey?: number|string) => boolean;\n\n interface LoDashStatic {\n /**\n * Performs a deep comparison between `object` and `source` to determine if\n * `object` contains equivalent property values.\n *\n * **Note:** This method supports comparing the same values as `_.isEqual`.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n * @example\n *\n * var object = { 'user': 'fred', 'age': 40 };\n *\n * _.isMatch(object, { 'age': 40 });\n * // => true\n *\n * _.isMatch(object, { 'age': 36 });\n * // => false\n */\n isMatch(object: Object, source: Object): boolean;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.isMatch\n */\n isMatch(source: Object): boolean;\n }\n\n //_.isMatchWith\n type isMatchWithCustomizer = (value: any, other: any, indexOrKey?: number|string) => boolean;\n\n interface LoDashStatic {\n /**\n * This method is like `_.isMatch` except that it accepts `customizer` which\n * is invoked to compare values. If `customizer` returns `undefined` comparisons\n * are handled by the method instead. The `customizer` is invoked with three\n * arguments: (objValue, srcValue, index|key, object, source).\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n * @example\n *\n * function isGreeting(value) {\n * return /^h(?:i|ello)$/.test(value);\n * }\n *\n * function customizer(objValue, srcValue) {\n * if (isGreeting(objValue) && isGreeting(srcValue)) {\n * return true;\n * }\n * }\n *\n * var object = { 'greeting': 'hello' };\n * var source = { 'greeting': 'hi' };\n *\n * _.isMatchWith(object, source, customizer);\n * // => true\n */\n isMatchWith(object: Object, source: Object, customizer: isMatchWithCustomizer): boolean;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.isMatchWith\n */\n isMatchWith(source: Object, customizer: isMatchWithCustomizer): boolean;\n }\n\n //_.isNaN\n interface LoDashStatic {\n /**\n * Checks if value is NaN.\n *\n * Note: This method is not the same as isNaN which returns true for undefined and other non-numeric values.\n *\n * @param value The value to check.\n * @return Returns true if value is NaN, else false.\n */\n isNaN(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isNaN\n */\n isNaN(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isNaN\n */\n isNaN(): LoDashExplicitWrapper;\n }\n\n //_.isNative\n interface LoDashStatic {\n /**\n * Checks if value is a native function.\n * @param value The value to check.\n *\n * @retrun Returns true if value is a native function, else false.\n */\n isNative(value: any): value is Function;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isNative\n */\n isNative(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isNative\n */\n isNative(): LoDashExplicitWrapper;\n }\n\n //_.isNil\n interface LoDashStatic {\n /**\n * Checks if `value` is `null` or `undefined`.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is nullish, else `false`.\n * @example\n *\n * _.isNil(null);\n * // => true\n *\n * _.isNil(void 0);\n * // => true\n *\n * _.isNil(NaN);\n * // => false\n */\n isNil(value: any): value is null | undefined;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isNil\n */\n isNil(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isNil\n */\n isNil(): LoDashExplicitWrapper;\n }\n\n //_.isNull\n interface LoDashStatic {\n /**\n * Checks if value is null.\n *\n * @param value The value to check.\n * @return Returns true if value is null, else false.\n */\n isNull(value: any): value is null;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isNull\n */\n isNull(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isNull\n */\n isNull(): LoDashExplicitWrapper;\n }\n\n //_.isNumber\n interface LoDashStatic {\n /**\n * Checks if value is classified as a Number primitive or object.\n *\n * Note: To exclude Infinity, -Infinity, and NaN, which are classified as numbers, use the _.isFinite method.\n *\n * @param value The value to check.\n * @return Returns true if value is correctly classified, else false.\n */\n isNumber(value?: any): value is number;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isNumber\n */\n isNumber(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isNumber\n */\n isNumber(): LoDashExplicitWrapper;\n }\n\n //_.isObject\n interface LoDashStatic {\n /**\n * Checks if value is the language type of Object. (e.g. arrays, functions, objects, regexes, new Number(0),\n * and new String(''))\n *\n * @param value The value to check.\n * @return Returns true if value is an object, else false.\n */\n isObject(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isObject\n */\n isObject(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isObject\n */\n isObject(): LoDashExplicitWrapper;\n }\n\n //_.isObjectLike\n interface LoDashStatic {\n /**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\n isObjectLike(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isObjectLike\n */\n isObjectLike(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isObjectLike\n */\n isObjectLike(): LoDashExplicitWrapper;\n }\n\n //_.isPlainObject\n interface LoDashStatic {\n /**\n * Checks if value is a plain object, that is, an object created by the Object constructor or one with a\n * [[Prototype]] of null.\n *\n * Note: This method assumes objects created by the Object constructor have no inherited enumerable properties.\n *\n * @param value The value to check.\n * @return Returns true if value is a plain object, else false.\n */\n isPlainObject(value?: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isPlainObject\n */\n isPlainObject(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isPlainObject\n */\n isPlainObject(): LoDashExplicitWrapper;\n }\n\n //_.isRegExp\n interface LoDashStatic {\n /**\n * Checks if value is classified as a RegExp object.\n * @param value The value to check.\n *\n * @return Returns true if value is correctly classified, else false.\n */\n isRegExp(value?: any): value is RegExp;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isRegExp\n */\n isRegExp(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isRegExp\n */\n isRegExp(): LoDashExplicitWrapper;\n }\n\n //_.isSafeInteger\n interface LoDashStatic {\n /**\n * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754\n * double precision number which isn't the result of a rounded unsafe integer.\n *\n * **Note:** This method is based on [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`.\n * @example\n *\n * _.isSafeInteger(3);\n * // => true\n *\n * _.isSafeInteger(Number.MIN_VALUE);\n * // => false\n *\n * _.isSafeInteger(Infinity);\n * // => false\n *\n * _.isSafeInteger('3');\n * // => false\n */\n isSafeInteger(value: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isSafeInteger\n */\n isSafeInteger(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isSafeInteger\n */\n isSafeInteger(): LoDashExplicitWrapper;\n }\n\n //_.isSet\n interface LoDashStatic {\n /**\n * Checks if value is classified as a Set object.\n *\n * @param value The value to check.\n * @returns Returns true if value is correctly classified, else false.\n */\n isSet(value?: any): value is Set;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isSet\n */\n isSet(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isSet\n */\n isSet(): LoDashExplicitWrapper;\n }\n\n //_.isString\n interface LoDashStatic {\n /**\n * Checks if value is classified as a String primitive or object.\n *\n * @param value The value to check.\n * @return Returns true if value is correctly classified, else false.\n */\n isString(value?: any): value is string;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isString\n */\n isString(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isString\n */\n isString(): LoDashExplicitWrapper;\n }\n\n //_.isSymbol\n interface LoDashStatic {\n /**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\n isSymbol(value: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isSymbol\n */\n isSymbol(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isSymbol\n */\n isSymbol(): LoDashExplicitWrapper;\n }\n\n //_.isTypedArray\n interface LoDashStatic {\n /**\n * Checks if value is classified as a typed array.\n *\n * @param value The value to check.\n * @return Returns true if value is correctly classified, else false.\n */\n isTypedArray(value: any): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isTypedArray\n */\n isTypedArray(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isTypedArray\n */\n isTypedArray(): LoDashExplicitWrapper;\n }\n\n //_.isUndefined\n interface LoDashStatic {\n /**\n * Checks if value is undefined.\n *\n * @param value The value to check.\n * @return Returns true if value is undefined, else false.\n */\n isUndefined(value: any): value is undefined;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * see _.isUndefined\n */\n isUndefined(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * see _.isUndefined\n */\n isUndefined(): LoDashExplicitWrapper;\n }\n\n //_.isWeakMap\n interface LoDashStatic {\n /**\n * Checks if value is classified as a WeakMap object.\n *\n * @param value The value to check.\n * @returns Returns true if value is correctly classified, else false.\n */\n isWeakMap(value?: any): value is WeakMap;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isSet\n */\n isWeakMap(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isSet\n */\n isWeakMap(): LoDashExplicitWrapper;\n }\n\n //_.isWeakSet\n interface LoDashStatic {\n /**\n * Checks if value is classified as a WeakSet object.\n *\n * @param value The value to check.\n * @returns Returns true if value is correctly classified, else false.\n */\n isWeakSet(value?: any): value is WeakSet;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.isWeakSet\n */\n isWeakSet(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.isWeakSet\n */\n isWeakSet(): LoDashExplicitWrapper;\n }\n\n //_.lt\n interface LoDashStatic {\n /**\n * Checks if value is less than other.\n *\n * @param value The value to compare.\n * @param other The other value to compare.\n * @return Returns true if value is less than other, else false.\n */\n lt(\n value: any,\n other: any\n ): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.lt\n */\n lt(other: any): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.lt\n */\n lt(other: any): LoDashExplicitWrapper;\n }\n\n //_.lte\n interface LoDashStatic {\n /**\n * Checks if value is less than or equal to other.\n *\n * @param value The value to compare.\n * @param other The other value to compare.\n * @return Returns true if value is less than or equal to other, else false.\n */\n lte(\n value: any,\n other: any\n ): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.lte\n */\n lte(other: any): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.lte\n */\n lte(other: any): LoDashExplicitWrapper;\n }\n\n //_.toArray\n interface LoDashStatic {\n /**\n * Converts value to an array.\n *\n * @param value The value to convert.\n * @return Returns the converted array.\n */\n toArray(value: List|Dictionary|NumericDictionary): T[];\n\n /**\n * @see _.toArray\n */\n toArray(value: TValue): TResult[];\n\n /**\n * @see _.toArray\n */\n toArray(value?: any): TResult[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.toArray\n */\n toArray(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.toArray\n */\n toArray(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.toArray\n */\n toArray(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.toArray\n */\n toArray(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.toArray\n */\n toArray(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.toArray\n */\n toArray(): LoDashExplicitArrayWrapper;\n }\n\n //_.toPlainObject\n interface LoDashStatic {\n /**\n * Converts value to a plain object flattening inherited enumerable properties of value to own properties\n * of the plain object.\n *\n * @param value The value to convert.\n * @return Returns the converted plain object.\n */\n toPlainObject(value?: any): TResult;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.toPlainObject\n */\n toPlainObject(): LoDashImplicitObjectWrapper;\n }\n\n //_.toFinite\n interface LoDashStatic {\n /**\n * Converts `value` to a finite number.\n *\n * @static\n * @memberOf _\n * @since 4.12.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted number.\n * @example\n *\n * _.toFinite(3.2);\n * // => 3.2\n *\n * _.toFinite(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toFinite(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toFinite('3.2');\n * // => 3.2\n */\n toFinite(value: any): number;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.toFinite\n */\n toFinite(): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.toFinite\n */\n toFinite(): LoDashExplicitWrapper;\n }\n\n //_.toInteger\n interface LoDashStatic {\n /**\n * Converts `value` to an integer.\n *\n * **Note:** This function is loosely based on [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toInteger(3);\n * // => 3\n *\n * _.toInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toInteger(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toInteger('3');\n * // => 3\n */\n toInteger(value: any): number;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.toInteger\n */\n toInteger(): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.toInteger\n */\n toInteger(): LoDashExplicitWrapper;\n }\n\n //_.toLength\n interface LoDashStatic {\n /**\n * Converts `value` to an integer suitable for use as the length of an\n * array-like object.\n *\n * **Note:** This method is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to convert.\n * @return {number} Returns the converted integer.\n * @example\n *\n * _.toLength(3);\n * // => 3\n *\n * _.toLength(Number.MIN_VALUE);\n * // => 0\n *\n * _.toLength(Infinity);\n * // => 4294967295\n *\n * _.toLength('3');\n * // => 3\n */\n toLength(value: any): number;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.toLength\n */\n toLength(): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.toLength\n */\n toLength(): LoDashExplicitWrapper;\n }\n\n //_.toNumber\n interface LoDashStatic {\n /**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3);\n * // => 3\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3');\n * // => 3\n */\n toNumber(value: any): number;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.toNumber\n */\n toNumber(): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.toNumber\n */\n toNumber(): LoDashExplicitWrapper;\n }\n\n //_.toSafeInteger\n interface LoDashStatic {\n /**\n * Converts `value` to a safe integer. A safe integer can be compared and\n * represented correctly.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toSafeInteger(3);\n * // => 3\n *\n * _.toSafeInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toSafeInteger(Infinity);\n * // => 9007199254740991\n *\n * _.toSafeInteger('3');\n * // => 3\n */\n toSafeInteger(value: any): number;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.toSafeInteger\n */\n toSafeInteger(): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.toSafeInteger\n */\n toSafeInteger(): LoDashExplicitWrapper;\n }\n\n //_.toString DUMMY\n interface LoDashStatic {\n /**\n * Converts `value` to a string if it's not one. An empty string is returned\n * for `null` and `undefined` values. The sign of `-0` is preserved.\n *\n * @static\n * @memberOf _\n * @category Lang\n * @param {*} value The value to process.\n * @returns {string} Returns the string.\n * @example\n *\n * _.toString(null);\n * // => ''\n *\n * _.toString(-0);\n * // => '-0'\n *\n * _.toString([1, 2, 3]);\n * // => '1,2,3'\n */\n toString(value: any): string;\n }\n\n /********\n * Math *\n ********/\n\n //_.add\n interface LoDashStatic {\n /**\n * Adds two numbers.\n *\n * @param augend The first number to add.\n * @param addend The second number to add.\n * @return Returns the sum.\n */\n add(\n augend: number,\n addend: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.add\n */\n add(addend: number): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.add\n */\n add(addend: number): LoDashExplicitWrapper;\n }\n\n //_.ceil\n interface LoDashStatic {\n /**\n * Calculates n rounded up to precision.\n *\n * @param n The number to round up.\n * @param precision The precision to round up to.\n * @return Returns the rounded up number.\n */\n ceil(\n n: number,\n precision?: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.ceil\n */\n ceil(precision?: number): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.ceil\n */\n ceil(precision?: number): LoDashExplicitWrapper;\n }\n\n //_.divide\n interface LoDashStatic {\n /**\n * Divide two numbers.\n *\n * @param dividend The first number in a division.\n * @param divisor The second number in a division.\n * @returns Returns the quotient.\n */\n divide(\n dividend: number,\n divisor: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.divide\n */\n divide(divisor: number): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.divide\n */\n divide(divisor: number): LoDashExplicitWrapper;\n }\n\n //_.floor\n interface LoDashStatic {\n /**\n * Calculates n rounded down to precision.\n *\n * @param n The number to round down.\n * @param precision The precision to round down to.\n * @return Returns the rounded down number.\n */\n floor(\n n: number,\n precision?: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.floor\n */\n floor(precision?: number): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.floor\n */\n floor(precision?: number): LoDashExplicitWrapper;\n }\n\n //_.max\n interface LoDashStatic {\n /**\n * Computes the maximum value of `array`. If `array` is empty or falsey\n * `undefined` is returned.\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @returns {*} Returns the maximum value.\n */\n max(\n collection: List | null | undefined\n ): T | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.max\n */\n max(): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.max\n */\n max(): T | undefined;\n }\n\n //_.maxBy\n interface LoDashStatic {\n /**\n * This method is like `_.max` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * the value is ranked. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {*} Returns the maximum value.\n * @example\n *\n * var objects = [{ 'n': 1 }, { 'n': 2 }];\n *\n * _.maxBy(objects, function(o) { return o.a; });\n * // => { 'n': 2 }\n *\n * // using the `_.property` iteratee shorthand\n * _.maxBy(objects, 'n');\n * // => { 'n': 2 }\n */\n maxBy(\n collection: List | null | undefined,\n iteratee?: ListIterator\n ): T | undefined;\n\n /**\n * @see _.maxBy\n */\n maxBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): T | undefined;\n\n /**\n * @see _.maxBy\n */\n maxBy(\n collection: List|Dictionary | null | undefined,\n iteratee?: string\n ): T | undefined;\n\n /**\n * @see _.maxBy\n */\n maxBy(\n collection: List|Dictionary | null | undefined,\n whereValue?: TObject\n ): T | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.maxBy\n */\n maxBy(\n iteratee?: ListIterator\n ): T | undefined;\n\n /**\n * @see _.maxBy\n */\n maxBy(\n iteratee?: string\n ): T | undefined;\n\n /**\n * @see _.maxBy\n */\n maxBy(\n whereValue?: TObject\n ): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.maxBy\n */\n maxBy(\n iteratee?: ListIterator|DictionaryIterator\n ): T | undefined;\n\n /**\n * @see _.maxBy\n */\n maxBy(\n iteratee?: string\n ): T | undefined;\n\n /**\n * @see _.maxBy\n */\n maxBy(\n whereValue?: TObject\n ): T | undefined;\n }\n\n //_.mean\n interface LoDashStatic {\n /**\n * Computes the mean of the values in `array`.\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @returns {number} Returns the mean.\n * @example\n *\n * _.mean([4, 2, 8, 6]);\n * // => 5\n */\n mean(\n collection: List | null | undefined\n ): number;\n }\n\n //_.meanBy\n interface LoDashStatic {\n /**\n * Computes the mean of the provided propties of the objects in the `array`\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the mean.\n * @example\n *\n * _.mean([{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }], 'n');\n * // => 5\n */\n meanBy(\n collection: List | null | undefined,\n iteratee?: ListIterator | string\n ): number;\n\n meanBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator | string\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.mean\n */\n mean(): number;\n\n /**\n * @see _.mean\n */\n mean(): number;\n }\n\n //_.min\n interface LoDashStatic {\n /**\n * Computes the minimum value of `array`. If `array` is empty or falsey\n * `undefined` is returned.\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @returns {*} Returns the minimum value.\n */\n min(\n collection: List | null | undefined\n ): T | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.min\n */\n min(): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.min\n */\n min(): T | undefined;\n }\n\n //_.minBy\n interface LoDashStatic {\n /**\n * This method is like `_.min` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * the value is ranked. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {*} Returns the minimum value.\n * @example\n *\n * var objects = [{ 'n': 1 }, { 'n': 2 }];\n *\n * _.minBy(objects, function(o) { return o.a; });\n * // => { 'n': 1 }\n *\n * // using the `_.property` iteratee shorthand\n * _.minBy(objects, 'n');\n * // => { 'n': 1 }\n */\n minBy(\n collection: List | null | undefined,\n iteratee?: ListIterator\n ): T | undefined;\n\n /**\n * @see _.minBy\n */\n minBy(\n collection: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): T | undefined;\n\n /**\n * @see _.minBy\n */\n minBy(\n collection: List|Dictionary | null | undefined,\n iteratee?: string\n ): T | undefined;\n\n /**\n * @see _.minBy\n */\n minBy(\n collection: List|Dictionary | null | undefined,\n whereValue?: TObject\n ): T | undefined;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.minBy\n */\n minBy(\n iteratee?: ListIterator\n ): T | undefined;\n\n /**\n * @see _.minBy\n */\n minBy(\n iteratee?: string\n ): T | undefined;\n\n /**\n * @see _.minBy\n */\n minBy(\n whereValue?: TObject\n ): T | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.minBy\n */\n minBy(\n iteratee?: ListIterator|DictionaryIterator\n ): T | undefined;\n\n /**\n * @see _.minBy\n */\n minBy(\n iteratee?: string\n ): T | undefined;\n\n /**\n * @see _.minBy\n */\n minBy(\n whereValue?: TObject\n ): T | undefined;\n }\n\n //_.multiply\n interface LoDashStatic {\n /**\n * Multiply two numbers.\n * @param multiplier The first number in a multiplication.\n * @param multiplicand The second number in a multiplication.\n * @returns Returns the product.\n */\n multiply(\n multiplier: number,\n multiplicand: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.multiply\n */\n multiply(multiplicand: number): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.multiply\n */\n multiply(multiplicand: number): LoDashExplicitWrapper;\n }\n\n //_.round\n interface LoDashStatic {\n /**\n * Calculates n rounded to precision.\n *\n * @param n The number to round.\n * @param precision The precision to round to.\n * @return Returns the rounded number.\n */\n round(\n n: number,\n precision?: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.round\n */\n round(precision?: number): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.round\n */\n round(precision?: number): LoDashExplicitWrapper;\n }\n\n //_.sum\n interface LoDashStatic {\n /**\n * Computes the sum of the values in `array`.\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @returns {number} Returns the sum.\n * @example\n *\n * _.sum([4, 2, 8, 6]);\n * // => 20\n */\n sum(collection: List | null | undefined): number;\n\n /**\n * @see _.sum\n */\n sum(collection: List|Dictionary | null | undefined): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sum\n */\n sum(): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sum\n **/\n sum(): number;\n\n /**\n * @see _.sum\n */\n sum(): number;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sum\n */\n sum(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sum\n */\n sum(): LoDashExplicitWrapper;\n\n /**\n * @see _.sum\n */\n sum(): LoDashExplicitWrapper;\n }\n\n //_.sumBy\n interface LoDashStatic {\n /**\n * This method is like `_.sum` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the value to be summed.\n * The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {Array} array The array to iterate over.\n * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the sum.\n * @example\n *\n * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];\n *\n * _.sumBy(objects, function(o) { return o.n; });\n * // => 20\n *\n * // using the `_.property` iteratee shorthand\n * _.sumBy(objects, 'n');\n * // => 20\n */\n sumBy(\n collection: List | null | undefined,\n iteratee: ListIterator\n ): number;\n\n /**\n * @see _.sumBy\n */\n sumBy(\n collection: List<{}> | null | undefined,\n iteratee: string\n ): number;\n\n /**\n * @see _.sumBy\n */\n sumBy(\n collection: List | null | undefined\n ): number;\n\n /**\n * @see _.sumBy\n */\n sumBy(\n collection: List<{}> | null | undefined,\n iteratee: Dictionary<{}>\n ): number;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.sumBy\n */\n sumBy(\n iteratee: ListIterator\n ): number;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: string): number;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: Dictionary<{}>): number;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.sumBy\n */\n sumBy(\n iteratee: ListIterator<{}, number>\n ): number;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: string): number;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: Dictionary<{}>): number;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.sumBy\n */\n sumBy(\n iteratee: ListIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: string): LoDashExplicitWrapper;\n\n /**\n * @see _.sumBy\n */\n sumBy(): LoDashExplicitWrapper;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: Dictionary<{}>): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.sumBy\n */\n sumBy(\n iteratee: ListIterator<{}, number>\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: string): LoDashExplicitWrapper;\n\n /**\n * @see _.sumBy\n */\n sumBy(iteratee: Dictionary<{}>): LoDashExplicitWrapper;\n }\n\n /**********\n * Number *\n **********/\n\n //_.subtract\n interface LoDashStatic {\n /**\n * Subtract two numbers.\n *\n * @static\n * @memberOf _\n * @category Math\n * @param {number} minuend The first number in a subtraction.\n * @param {number} subtrahend The second number in a subtraction.\n * @returns {number} Returns the difference.\n * @example\n *\n * _.subtract(6, 4);\n * // => 2\n */\n subtract(\n minuend: number,\n subtrahend: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.subtract\n */\n subtract(\n subtrahend: number\n ): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.subtract\n */\n subtract(\n subtrahend: number\n ): LoDashExplicitWrapper;\n }\n\n //_.clamp\n interface LoDashStatic {\n /**\n * Clamps `number` within the inclusive `lower` and `upper` bounds.\n *\n * @static\n * @memberOf _\n * @category Number\n * @param {number} number The number to clamp.\n * @param {number} [lower] The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the clamped number.\n * @example\n *\n * _.clamp(-10, -5, 5);\n * // => -5\n *\n * _.clamp(10, -5, 5);\n * // => 5\n */\n clamp(\n number: number,\n lower: number,\n upper: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.clamp\n */\n clamp(\n lower: number,\n upper: number\n ): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.clamp\n */\n clamp(\n lower: number,\n upper: number\n ): LoDashExplicitWrapper;\n }\n\n //_.inRange\n interface LoDashStatic {\n /**\n * Checks if n is between start and up to but not including, end. If end is not specified it’s set to start\n * with start then set to 0.\n *\n * @param n The number to check.\n * @param start The start of the range.\n * @param end The end of the range.\n * @return Returns true if n is in the range, else false.\n */\n inRange(\n n: number,\n start: number,\n end: number\n ): boolean;\n\n /**\n * @see _.inRange\n */\n inRange(\n n: number,\n end: number\n ): boolean;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.inRange\n */\n inRange(\n start: number,\n end: number\n ): boolean;\n\n /**\n * @see _.inRange\n */\n inRange(end: number): boolean;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.inRange\n */\n inRange(\n start: number,\n end: number\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.inRange\n */\n inRange(end: number): LoDashExplicitWrapper;\n }\n\n //_.random\n interface LoDashStatic {\n /**\n * Produces a random number between min and max (inclusive). If only one argument is provided a number between\n * 0 and the given number is returned. If floating is true, or either min or max are floats, a floating-point\n * number is returned instead of an integer.\n *\n * @param min The minimum possible value.\n * @param max The maximum possible value.\n * @param floating Specify returning a floating-point number.\n * @return Returns the random number.\n */\n random(\n min?: number,\n max?: number,\n floating?: boolean\n ): number;\n\n /**\n * @see _.random\n */\n random(\n min?: number,\n floating?: boolean\n ): number;\n\n /**\n * @see _.random\n */\n random(floating?: boolean): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.random\n */\n random(\n max?: number,\n floating?: boolean\n ): number;\n\n /**\n * @see _.random\n */\n random(floating?: boolean): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.random\n */\n random(\n max?: number,\n floating?: boolean\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.random\n */\n random(floating?: boolean): LoDashExplicitWrapper;\n }\n\n /**********\n * Object *\n **********/\n\n //_.assign\n interface LoDashStatic {\n /**\n * Assigns own enumerable properties of source objects to the destination\n * object. Source objects are applied from left to right. Subsequent sources\n * overwrite property assignments of previous sources.\n *\n * **Note:** This method mutates `object` and is loosely based on\n * [`Object.assign`](https://mdn.io/Object/assign).\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @example\n *\n * function Foo() {\n * this.c = 3;\n * }\n *\n * function Bar() {\n * this.e = 5;\n * }\n *\n * Foo.prototype.d = 4;\n * Bar.prototype.f = 6;\n *\n * _.assign({ 'a': 1 }, new Foo, new Bar);\n * // => { 'a': 1, 'c': 3, 'e': 5 }\n */\n assign(\n object: TObject,\n source: TSource\n ): TObject & TSource;\n\n /**\n * @see assign\n */\n assign(\n object: TObject,\n source1: TSource1,\n source2: TSource2\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see assign\n */\n assign(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see assign\n */\n assign(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.assign\n */\n assign(object: TObject): TObject;\n\n /**\n * @see _.assign\n */\n assign(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.assign\n */\n assign(\n source: TSource\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assign\n */\n assign(\n source1: TSource1,\n source2: TSource2\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assign\n */\n assign(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assign\n */\n assign(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assign\n */\n assign(): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assign\n */\n assign(...otherArgs: any[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.assign\n */\n assign(\n source: TSource\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assign\n */\n assign(\n source1: TSource1,\n source2: TSource2\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assign\n */\n assign(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assign\n */\n assign(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assign\n */\n assign(): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assign\n */\n assign(...otherArgs: any[]): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashStatic {\n /**\n * This method is like `_.assign` except that it accepts `customizer` which\n * is invoked to produce the assigned values. If `customizer` returns `undefined`\n * assignment is handled by the method instead. The `customizer` is invoked\n * with five arguments: (objValue, srcValue, key, object, source).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * function customizer(objValue, srcValue) {\n * return _.isUndefined(objValue) ? srcValue : objValue;\n * }\n *\n * var defaults = _.partialRight(_.assignWith, customizer);\n *\n * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n assignWith(\n object: TObject,\n source: TSource,\n customizer: AssignCustomizer\n ): TObject & TSource;\n\n /**\n * @see assignWith\n */\n assignWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see assignWith\n */\n assignWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see assignWith\n */\n assignWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.assignWith\n */\n assignWith(object: TObject): TObject;\n\n /**\n * @see _.assignWith\n */\n assignWith(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.assignWith\n */\n assignWith(\n source: TSource,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignWith\n */\n assignWith(\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignWith\n */\n assignWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignWith\n */\n assignWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignWith\n */\n assignWith(): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignWith\n */\n assignWith(...otherArgs: any[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.assignWith\n */\n assignWith(\n source: TSource,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignWith\n */\n assignWith(\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignWith\n */\n assignWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignWith\n */\n assignWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignWith\n */\n assignWith(): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignWith\n */\n assignWith(...otherArgs: any[]): LoDashExplicitObjectWrapper;\n }\n\n //_.assignIn\n interface LoDashStatic {\n /**\n * This method is like `_.assign` except that it iterates over own and\n * inherited source properties.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @alias extend\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @example\n *\n * function Foo() {\n * this.b = 2;\n * }\n *\n * function Bar() {\n * this.d = 4;\n * }\n *\n * Foo.prototype.c = 3;\n * Bar.prototype.e = 5;\n *\n * _.assignIn({ 'a': 1 }, new Foo, new Bar);\n * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5 }\n */\n assignIn(\n object: TObject,\n source: TSource\n ): TObject & TSource;\n\n /**\n * @see assignIn\n */\n assignIn(\n object: TObject,\n source1: TSource1,\n source2: TSource2\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see assignIn\n */\n assignIn(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see assignIn\n */\n assignIn(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.assignIn\n */\n assignIn(object: TObject): TObject;\n\n /**\n * @see _.assignIn\n */\n assignIn(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.assignIn\n */\n assignIn(\n source: TSource\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignIn\n */\n assignIn(\n source1: TSource1,\n source2: TSource2\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignIn\n */\n assignIn(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignIn\n */\n assignIn(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n assignIn(): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n assignIn(...otherArgs: any[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.assignIn\n */\n assignIn(\n source: TSource\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignIn\n */\n assignIn(\n source1: TSource1,\n source2: TSource2\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignIn\n */\n assignIn(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignIn\n */\n assignIn(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n assignIn(): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n assignIn(...otherArgs: any[]): LoDashExplicitObjectWrapper;\n }\n\n //_.assignInWith\n type AssignCustomizer = (objectValue: any, sourceValue: any, key?: string, object?: {}, source?: {}) => any;\n\n interface LoDashStatic {\n /**\n * This method is like `_.assignIn` except that it accepts `customizer` which\n * is invoked to produce the assigned values. If `customizer` returns `undefined`\n * assignment is handled by the method instead. The `customizer` is invoked\n * with five arguments: (objValue, srcValue, key, object, source).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @alias extendWith\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * function customizer(objValue, srcValue) {\n * return _.isUndefined(objValue) ? srcValue : objValue;\n * }\n *\n * var defaults = _.partialRight(_.assignInWith, customizer);\n *\n * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n assignInWith(\n object: TObject,\n source: TSource,\n customizer: AssignCustomizer\n ): TObject & TSource;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.assignInWith\n */\n assignInWith(object: TObject): TObject;\n\n /**\n * @see _.assignInWith\n */\n assignInWith(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.assignInWith\n */\n assignInWith(\n source: TSource,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n assignInWith(): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n assignInWith(...otherArgs: any[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.assignInWith\n */\n assignInWith(\n source: TSource,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see assignInWith\n */\n assignInWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n assignInWith(): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n assignInWith(...otherArgs: any[]): LoDashExplicitObjectWrapper;\n }\n\n //_.create\n interface LoDashStatic {\n /**\n * Creates an object that inherits from the given prototype object. If a properties object is provided its own\n * enumerable properties are assigned to the created object.\n *\n * @param prototype The object to inherit from.\n * @param properties The properties to assign to the object.\n * @return Returns the new object.\n */\n create(\n prototype: T,\n properties?: U\n ): T & U;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.create\n */\n create(properties?: U): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.create\n */\n create(properties?: U): LoDashExplicitObjectWrapper;\n }\n\n //_.defaults\n interface LoDashStatic {\n /**\n * Assigns own enumerable properties of source object(s) to the destination object for all destination\n * properties that resolve to undefined. Once a property is set, additional values of the same property are\n * ignored.\n *\n * Note: This method mutates object.\n *\n * @param object The destination object.\n * @param sources The source objects.\n * @return The destination object.\n */\n defaults(\n object: TObject,\n source: TSource\n ): TSource & TObject;\n\n /**\n * @see _.defaults\n */\n defaults(\n object: TObject,\n source1: TSource1,\n source2: TSource2\n ): TSource2 & TSource1 & TObject;\n\n /**\n * @see _.defaults\n */\n defaults(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): TSource3 & TSource2 & TSource1 & TObject;\n\n /**\n * @see _.defaults\n */\n defaults(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): TSource4 & TSource3 & TSource2 & TSource1 & TObject;\n\n /**\n * @see _.defaults\n */\n defaults(object: TObject): TObject;\n\n /**\n * @see _.defaults\n */\n defaults(\n object: any,\n ...sources: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.defaults\n */\n defaults(\n source: TSource\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(\n source1: TSource1,\n source2: TSource2\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(...sources: any[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.defaults\n */\n defaults(\n source: TSource\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(\n source1: TSource1,\n source2: TSource2\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.defaults\n */\n defaults(...sources: any[]): LoDashExplicitObjectWrapper;\n }\n\n //_.defaultsDeep\n interface LoDashStatic {\n /**\n * This method is like _.defaults except that it recursively assigns default properties.\n * @param object The destination object.\n * @param sources The source objects.\n * @return Returns object.\n **/\n defaultsDeep(\n object: T,\n ...sources: any[]): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.defaultsDeep\n **/\n defaultsDeep(...sources: any[]): LoDashImplicitObjectWrapper;\n }\n\n // _.extend\n interface LoDashStatic {\n /**\n * @see _.assignIn\n */\n extend(\n object: TObject,\n source: TSource\n ): TObject & TSource;\n\n /**\n * @see _.assignIn\n */\n extend(\n object: TObject,\n source1: TSource1,\n source2: TSource2\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see _.assignIn\n */\n extend(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see _.assignIn\n */\n extend(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.assignIn\n */\n extend(object: TObject): TObject;\n\n /**\n * @see _.assignIn\n */\n extend(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.assignIn\n */\n extend(\n source: TSource\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(\n source1: TSource1,\n source2: TSource2\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(...otherArgs: any[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.assignIn\n */\n extend(\n source: TSource\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(\n source1: TSource1,\n source2: TSource2\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignIn\n */\n extend(...otherArgs: any[]): LoDashExplicitObjectWrapper;\n }\n\n interface LoDashStatic {\n /**\n * @see _.assignInWith\n */\n extendWith(\n object: TObject,\n source: TSource,\n customizer: AssignCustomizer\n ): TObject & TSource;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.assignInWith\n */\n extendWith(object: TObject): TObject;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.assignInWith\n */\n extendWith(\n source: TSource,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(...otherArgs: any[]): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.assignInWith\n */\n extendWith(\n source: TSource,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n source1: TSource1,\n source2: TSource2,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: AssignCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.assignInWith\n */\n extendWith(...otherArgs: any[]): LoDashExplicitObjectWrapper;\n }\n\n //_.findKey\n interface LoDashStatic {\n /**\n * This method is like _.find except that it returns the key of the first element predicate returns truthy for\n * instead of the element itself.\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param object The object to search.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the key of the matched element, else undefined.\n */\n findKey(\n object: TObject,\n predicate?: DictionaryIterator\n ): string | undefined;\n\n /**\n * @see _.findKey\n */\n findKey(\n object: TObject,\n predicate?: ObjectIterator\n ): string | undefined;\n\n /**\n * @see _.findKey\n */\n findKey(\n object: TObject,\n predicate?: string\n ): string | undefined;\n\n /**\n * @see _.findKey\n */\n findKey, TObject>(\n object: TObject,\n predicate?: TWhere\n ): string | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.findKey\n */\n findKey(\n predicate?: DictionaryIterator\n ): string | undefined;\n\n /**\n * @see _.findKey\n */\n findKey(\n predicate?: ObjectIterator\n ): string | undefined;\n\n /**\n * @see _.findKey\n */\n findKey(\n predicate?: string\n ): string | undefined;\n\n /**\n * @see _.findKey\n */\n findKey>(\n predicate?: TWhere\n ): string | undefined;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.findKey\n */\n findKey(\n predicate?: DictionaryIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findKey\n */\n findKey(\n predicate?: ObjectIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findKey\n */\n findKey(\n predicate?: string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findKey\n */\n findKey>(\n predicate?: TWhere\n ): LoDashExplicitWrapper;\n }\n\n //_.findLastKey\n interface LoDashStatic {\n /**\n * This method is like _.findKey except that it iterates over elements of a collection in the opposite order.\n *\n * If a property name is provided for predicate the created _.property style callback returns the property\n * value of the given element.\n *\n * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for\n * elements that have a matching property value, else false.\n *\n * If an object is provided for predicate the created _.matches style callback returns true for elements that\n * have the properties of the given object, else false.\n *\n * @param object The object to search.\n * @param predicate The function invoked per iteration.\n * @param thisArg The this binding of predicate.\n * @return Returns the key of the matched element, else undefined.\n */\n findLastKey(\n object: TObject,\n predicate?: DictionaryIterator\n ): string | undefined;\n\n /**\n * @see _.findLastKey\n */\n findLastKey(\n object: TObject,\n predicate?: ObjectIterator\n ): string | undefined;\n\n /**\n * @see _.findLastKey\n */\n findLastKey(\n object: TObject,\n predicate?: string\n ): string;\n\n /**\n * @see _.findLastKey\n */\n findLastKey, TObject>(\n object: TObject,\n predicate?: TWhere\n ): string | undefined;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.findLastKey\n */\n findLastKey(\n predicate?: DictionaryIterator\n ): string;\n\n /**\n * @see _.findLastKey\n */\n findLastKey(\n predicate?: ObjectIterator\n ): string | undefined;\n\n /**\n * @see _.findLastKey\n */\n findLastKey(\n predicate?: string\n ): string | undefined;\n\n /**\n * @see _.findLastKey\n */\n findLastKey>(\n predicate?: TWhere\n ): string | undefined;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.findLastKey\n */\n findLastKey(\n predicate?: DictionaryIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findLastKey\n */\n findLastKey(\n predicate?: ObjectIterator\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findLastKey\n */\n findLastKey(\n predicate?: string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.findLastKey\n */\n findLastKey>(\n predicate?: TWhere\n ): LoDashExplicitWrapper;\n }\n\n //_.forIn\n interface LoDashStatic {\n /**\n * Iterates over own and inherited enumerable properties of an object invoking iteratee for each property. The\n * iteratee is bound to thisArg and invoked with three arguments: (value, key, object). Iteratee functions may\n * exit iteration early by explicitly returning false.\n *\n * @param object The object to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns object.\n */\n forIn(\n object: Dictionary,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.forIn\n */\n forIn(\n object: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary | null | undefined;\n\n /**\n * @see _.forIn\n */\n forIn(\n object: T,\n iteratee?: ObjectIterator\n ): T;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forIn\n */\n forIn(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forIn\n */\n forIn(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n //_.forInRight\n interface LoDashStatic {\n /**\n * This method is like _.forIn except that it iterates over properties of object in the opposite order.\n *\n * @param object The object to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns object.\n */\n forInRight(\n object: Dictionary,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.forInRight\n */\n forInRight(\n object: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary | null | undefined;\n\n /**\n * @see _.forInRight\n */\n forInRight(\n object: T,\n iteratee?: ObjectIterator\n ): T;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forInRight\n */\n forInRight(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forInRight\n */\n forInRight(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n //_.forOwn\n interface LoDashStatic {\n /**\n * Iterates over own enumerable properties of an object invoking iteratee for each property. The iteratee is\n * bound to thisArg and invoked with three arguments: (value, key, object). Iteratee functions may exit\n * iteration early by explicitly returning false.\n *\n * @param object The object to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns object.\n */\n forOwn(\n object: Dictionary,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.forOwn\n */\n forOwn(\n object: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary | null | undefined;\n\n /**\n * @see _.forOwn\n */\n forOwn(\n object: T,\n iteratee?: ObjectIterator\n ): T;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forOwn\n */\n forOwn(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forOwn\n */\n forOwn(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n //_.forOwnRight\n interface LoDashStatic {\n /**\n * This method is like _.forOwn except that it iterates over properties of object in the opposite order.\n *\n * @param object The object to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns object.\n */\n forOwnRight(\n object: Dictionary,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.forOwnRight\n */\n forOwnRight(\n object: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary | null | undefined;\n\n /**\n * @see _.forOwnRight\n */\n forOwnRight(\n object: T,\n iteratee?: ObjectIterator\n ): T;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.forOwnRight\n */\n forOwnRight(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.forOwnRight\n */\n forOwnRight(\n iteratee?: DictionaryIterator\n ): TWrapper;\n }\n\n //_.functions\n interface LoDashStatic {\n /**\n * Creates an array of function property names from own enumerable properties\n * of `object`.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The object to inspect.\n * @returns {Array} Returns the new array of property names.\n * @example\n *\n * function Foo() {\n * this.a = _.constant('a');\n * this.b = _.constant('b');\n * }\n *\n * Foo.prototype.c = _.constant('c');\n *\n * _.functions(new Foo);\n * // => ['a', 'b']\n */\n functions(object: any): string[];\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.functions\n */\n functions(): _.LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.functions\n */\n functions(): _.LoDashExplicitArrayWrapper;\n }\n\n //_.functionsIn\n interface LoDashStatic {\n /**\n * Creates an array of function property names from own and inherited\n * enumerable properties of `object`.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The object to inspect.\n * @returns {Array} Returns the new array of property names.\n * @example\n *\n * function Foo() {\n * this.a = _.constant('a');\n * this.b = _.constant('b');\n * }\n *\n * Foo.prototype.c = _.constant('c');\n *\n * _.functionsIn(new Foo);\n * // => ['a', 'b', 'c']\n */\n functionsIn(object: any): string[];\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.functionsIn\n */\n functionsIn(): _.LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.functionsIn\n */\n functionsIn(): _.LoDashExplicitArrayWrapper;\n }\n\n //_.get\n interface LoDashStatic {\n /**\n * Gets the property value at path of object. If the resolved value is undefined the defaultValue is used\n * in its place.\n *\n * @param object The object to query.\n * @param path The path of the property to get.\n * @param defaultValue The value returned if the resolved value is undefined.\n * @return Returns the resolved value.\n */\n get(\n object: TObject,\n path: Many,\n defaultValue?: TResult\n ): TResult;\n\n /**\n * @see _.get\n */\n get(\n object: any,\n path: Many,\n defaultValue?: TResult\n ): TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.get\n */\n get(\n path: Many,\n defaultValue?: TResult\n ): TResult;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.get\n */\n get(\n path: Many,\n defaultValue?: TResult\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.get\n */\n get(\n path: Many,\n defaultValue?: TResult\n ): TResult;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.get\n */\n get(\n path: Many,\n defaultValue?: any\n ): TResultWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.get\n */\n get(\n path: Many,\n defaultValue?: any\n ): TResultWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.get\n */\n get(\n path: Many,\n defaultValue?: any\n ): TResultWrapper;\n }\n\n //_.has\n interface LoDashStatic {\n /**\n * Checks if `path` is a direct property of `object`.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = { 'a': { 'b': { 'c': 3 } } };\n * var other = _.create({ 'a': _.create({ 'b': _.create({ 'c': 3 }) }) });\n *\n * _.has(object, 'a');\n * // => true\n *\n * _.has(object, 'a.b.c');\n * // => true\n *\n * _.has(object, ['a', 'b', 'c']);\n * // => true\n *\n * _.has(other, 'a');\n * // => false\n */\n has(\n object: T,\n path: Many\n ): boolean;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.has\n */\n has(path: Many): boolean;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.has\n */\n has(path: Many): LoDashExplicitWrapper;\n }\n\n //_.hasIn\n interface LoDashStatic {\n /**\n * Checks if `path` is a direct or inherited property of `object`.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = _.create({ 'a': _.create({ 'b': _.create({ 'c': 3 }) }) });\n *\n * _.hasIn(object, 'a');\n * // => true\n *\n * _.hasIn(object, 'a.b.c');\n * // => true\n *\n * _.hasIn(object, ['a', 'b', 'c']);\n * // => true\n *\n * _.hasIn(object, 'b');\n * // => false\n */\n hasIn(\n object: T,\n path: Many\n ): boolean;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.hasIn\n */\n hasIn(path: Many): boolean;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.hasIn\n */\n hasIn(path: Many): LoDashExplicitWrapper;\n }\n\n //_.invert\n interface LoDashStatic {\n /**\n * Creates an object composed of the inverted keys and values of object. If object contains duplicate values,\n * subsequent values overwrite property assignments of previous values unless multiValue is true.\n *\n * @param object The object to invert.\n * @param multiValue Allow multiple values per key.\n * @return Returns the new inverted object.\n */\n invert(\n object: T,\n multiValue?: boolean\n ): TResult;\n\n /**\n * @see _.invert\n */\n invert(\n object: Object,\n multiValue?: boolean\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.invert\n */\n invert(multiValue?: boolean): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.invert\n */\n invert(multiValue?: boolean): LoDashExplicitObjectWrapper;\n }\n\n //_.inverBy\n type InvertByIterator = (value: T) => any;\n\n interface LoDashStatic {\n /**\n * This method is like _.invert except that the inverted object is generated from the results of running each\n * element of object through iteratee. The corresponding inverted value of each inverted key is an array of\n * keys responsible for generating the inverted value. The iteratee is invoked with one argument: (value).\n *\n * @param object The object to invert.\n * @param interatee The iteratee invoked per element.\n * @return Returns the new inverted object.\n */\n invertBy(\n object: Object,\n interatee?: InvertByIterator|string\n ): Dictionary;\n\n /**\n * @see _.invertBy\n */\n invertBy(\n object: _.Dictionary|_.NumericDictionary,\n interatee?: InvertByIterator|string\n ): Dictionary;\n\n /**\n * @see _.invertBy\n */\n invertBy(\n object: Object,\n interatee?: W\n ): Dictionary;\n\n /**\n * @see _.invertBy\n */\n invertBy(\n object: _.Dictionary,\n interatee?: W\n ): Dictionary;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: InvertByIterator\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: InvertByIterator|string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: W\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: InvertByIterator|string\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: W\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: InvertByIterator\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: InvertByIterator|string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: W\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: InvertByIterator|string\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.invertBy\n */\n invertBy(\n interatee?: W\n ): LoDashExplicitObjectWrapper>;\n }\n\n //_.keys\n interface LoDashStatic {\n /**\n * Creates an array of the own enumerable property names of object.\n *\n * Note: Non-object values are coerced to objects. See the ES spec for more details.\n *\n * @param object The object to query.\n * @return Returns the array of property names.\n */\n keys(object?: any): string[];\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.keys\n */\n keys(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.keys\n */\n keys(): LoDashExplicitArrayWrapper;\n }\n\n //_.keysIn\n interface LoDashStatic {\n /**\n * Creates an array of the own and inherited enumerable property names of object.\n *\n * Note: Non-object values are coerced to objects.\n *\n * @param object The object to query.\n * @return An array of property names.\n */\n keysIn(object?: any): string[];\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.keysIn\n */\n keysIn(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.keysIn\n */\n keysIn(): LoDashExplicitArrayWrapper;\n }\n\n //_.mapKeys\n interface LoDashStatic {\n /**\n * The opposite of _.mapValues; this method creates an object with the same values as object and keys generated\n * by running each own enumerable property of object through iteratee.\n *\n * @param object The object to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param thisArg The this binding of iteratee.\n * @return Returns the new mapped object.\n */\n mapKeys(\n object: List | null | undefined,\n iteratee?: ListIterator\n ): Dictionary;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n object: Dictionary | null | undefined,\n iteratee?: DictionaryIterator\n ): Dictionary;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n object: List|Dictionary | null | undefined,\n iteratee?: TObject\n ): Dictionary;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n object: List|Dictionary | null | undefined,\n iteratee?: string\n ): Dictionary;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: ListIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: TObject\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: TObject\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: string\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: ListIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: TObject\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: ListIterator|DictionaryIterator\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: TObject\n ): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.mapKeys\n */\n mapKeys(\n iteratee?: string\n ): LoDashExplicitObjectWrapper>;\n }\n\n //_.mapValues\n interface LoDashStatic {\n /**\n * Creates an object with the same keys as object and values generated by running each own\n * enumerable property of object through iteratee. The iteratee function is bound to thisArg\n * and invoked with three arguments: (value, key, object).\n *\n * If a property name is provided iteratee the created \"_.property\" style callback returns\n * the property value of the given element.\n *\n * If a value is also provided for thisArg the creted \"_.matchesProperty\" style callback returns\n * true for elements that have a matching property value, else false;.\n *\n * If an object is provided for iteratee the created \"_.matches\" style callback returns true\n * for elements that have the properties of the given object, else false.\n *\n * @param {Object} object The object to iterate over.\n * @param {Function|Object|string} [iteratee=_.identity] The function invoked per iteration.\n * @param {Object} [thisArg] The `this` binding of `iteratee`.\n * @return {Object} Returns the new mapped object.\n */\n mapValues(obj: Dictionary | null | undefined, callback: ObjectIterator): Dictionary;\n mapValues(obj: Dictionary | null | undefined, where: Dictionary): Dictionary;\n mapValues(obj: T | null | undefined, pluck: string): TMapped;\n mapValues(obj: T | null | undefined, callback: ObjectIterator): T;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.mapValues\n * TValue is the type of the property values of T.\n * TResult is the type output by the ObjectIterator function\n */\n mapValues(callback: ObjectIterator): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.mapValues\n * TResult is the type of the property specified by pluck.\n * T should be a Dictionary>\n */\n mapValues(pluck: string): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.mapValues\n * TResult is the type of the properties of each object in the values of T\n * T should be a Dictionary>\n */\n mapValues(where: Dictionary): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.mapValues\n * TValue is the type of the property values of T.\n * TResult is the type output by the ObjectIterator function\n */\n mapValues(callback: ObjectIterator): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.mapValues\n * TResult is the type of the property specified by pluck.\n * T should be a Dictionary>\n */\n mapValues(pluck: string): LoDashExplicitObjectWrapper>;\n\n /**\n * @see _.mapValues\n * TResult is the type of the properties of each object in the values of T\n * T should be a Dictionary>\n */\n mapValues(where: Dictionary): LoDashExplicitObjectWrapper;\n }\n\n //_.merge\n interface LoDashStatic {\n /**\n * Recursively merges own and inherited enumerable properties of source\n * objects into the destination object, skipping source properties that resolve\n * to `undefined`. Array and plain object properties are merged recursively.\n * Other objects and value types are overridden by assignment. Source objects\n * are applied from left to right. Subsequent sources overwrite property\n * assignments of previous sources.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var users = {\n * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]\n * };\n *\n * var ages = {\n * 'data': [{ 'age': 36 }, { 'age': 40 }]\n * };\n *\n * _.merge(users, ages);\n * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }\n */\n merge(\n object: TObject,\n source: TSource\n ): TObject & TSource;\n\n /**\n * @see _.merge\n */\n merge(\n object: TObject,\n source1: TSource1,\n source2: TSource2\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see _.merge\n */\n merge(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see _.merge\n */\n merge(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.merge\n */\n merge(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.merge\n */\n merge(\n source: TSource\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n source1: TSource1,\n source2: TSource2\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n ...otherArgs: any[]\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.merge\n */\n merge(\n source: TSource\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n source1: TSource1,\n source2: TSource2\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.merge\n */\n merge(\n ...otherArgs: any[]\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.mergeWith\n type MergeWithCustomizer = (value: any, srcValue: any, key?: string, object?: Object, source?: Object) => any;\n\n interface LoDashStatic {\n /**\n * This method is like `_.merge` except that it accepts `customizer` which\n * is invoked to produce the merged values of the destination and source\n * properties. If `customizer` returns `undefined` merging is handled by the\n * method instead. The `customizer` is invoked with seven arguments:\n * (objValue, srcValue, key, object, source, stack).\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} customizer The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * function customizer(objValue, srcValue) {\n * if (_.isArray(objValue)) {\n * return objValue.concat(srcValue);\n * }\n * }\n *\n * var object = {\n * 'fruits': ['apple'],\n * 'vegetables': ['beet']\n * };\n *\n * var other = {\n * 'fruits': ['banana'],\n * 'vegetables': ['carrot']\n * };\n *\n * _.merge(object, other, customizer);\n * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }\n */\n mergeWith(\n object: TObject,\n source: TSource,\n customizer: MergeWithCustomizer\n ): TObject & TSource;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n customizer: MergeWithCustomizer\n ): TObject & TSource1 & TSource2;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: MergeWithCustomizer\n ): TObject & TSource1 & TSource2 & TSource3;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n object: TObject,\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: MergeWithCustomizer\n ): TObject & TSource1 & TSource2 & TSource3 & TSource4;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n object: any,\n ...otherArgs: any[]\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.mergeWith\n */\n mergeWith(\n source: TSource,\n customizer: MergeWithCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n source1: TSource1,\n source2: TSource2,\n customizer: MergeWithCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n customizer: MergeWithCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n source1: TSource1,\n source2: TSource2,\n source3: TSource3,\n source4: TSource4,\n customizer: MergeWithCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.mergeWith\n */\n mergeWith(\n ...otherArgs: any[]\n ): LoDashImplicitObjectWrapper;\n }\n\n //_.omit\n interface LoDashStatic {\n /**\n * The opposite of `_.pick`; this method creates an object composed of the\n * own and inherited enumerable properties of `object` that are not omitted.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [props] The property names to omit, specified\n * individually or in arrays..\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omit(object, ['a', 'c']);\n * // => { 'b': '2' }\n */\n\n omit(\n object: T | null | undefined,\n ...predicate: Array>\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.omit\n */\n omit(\n ...predicate: Array>\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.omit\n */\n omit(\n ...predicate: Array>\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.omitBy\n interface LoDashStatic {\n /**\n * The opposite of `_.pickBy`; this method creates an object composed of the\n * own and inherited enumerable properties of `object` that `predicate`\n * doesn't return truthy for.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {Function|Object|string} [predicate=_.identity] The function invoked per property.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omitBy(object, _.isNumber);\n * // => { 'b': '2' }\n */\n omitBy(\n object: T | null | undefined,\n predicate: ObjectIterator\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.omitBy\n */\n omitBy(\n predicate: ObjectIterator\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.omitBy\n */\n omitBy(\n predicate: ObjectIterator\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.pick\n interface LoDashStatic {\n /**\n * Creates an object composed of the picked `object` properties.\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [props] The property names to pick, specified\n * individually or in arrays.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.pick(object, ['a', 'c']);\n * // => { 'a': 1, 'c': 3 }\n */\n pick(\n object: T | null | undefined,\n ...predicate: Array>\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.pick\n */\n pick(\n ...predicate: Array>\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.pick\n */\n pick(\n ...predicate: Array>\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.pickBy\n interface LoDashStatic {\n /**\n * Creates an object composed of the `object` properties `predicate` returns\n * truthy for. The predicate is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {Function|Object|string} [predicate=_.identity] The function invoked per property.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.pickBy(object, _.isNumber);\n * // => { 'a': 1, 'c': 3 }\n */\n pickBy(\n object: T | null | undefined,\n predicate?: ObjectIterator\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.pickBy\n */\n pickBy(\n predicate?: ObjectIterator\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.pickBy\n */\n pickBy(\n predicate?: ObjectIterator\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.result\n interface LoDashStatic {\n /**\n * This method is like _.get except that if the resolved value is a function it’s invoked with the this binding\n * of its parent object and its result is returned.\n *\n * @param object The object to query.\n * @param path The path of the property to resolve.\n * @param defaultValue The value returned if the resolved value is undefined.\n * @return Returns the resolved value.\n */\n result(\n object: TObject,\n path: Many,\n defaultValue?: TResult|((...args: any[]) => TResult)\n ): TResult;\n\n /**\n * @see _.result\n */\n result(\n object: any,\n path: Many,\n defaultValue?: TResult|((...args: any[]) => TResult)\n ): TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.result\n */\n result(\n path: Many,\n defaultValue?: TResult|((...args: any[]) => TResult)\n ): TResult;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.result\n */\n result(\n path: Many,\n defaultValue?: TResult|((...args: any[]) => TResult)\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.result\n */\n result(\n path: Many,\n defaultValue?: TResult|((...args: any[]) => TResult)\n ): TResult;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.result\n */\n result(\n path: Many,\n defaultValue?: any\n ): TResultWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.result\n */\n result(\n path: Many,\n defaultValue?: any\n ): TResultWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.result\n */\n result(\n path: Many,\n defaultValue?: any\n ): TResultWrapper;\n }\n\n //_.set\n interface LoDashStatic {\n /**\n * Sets the value at path of object. If a portion of path doesn’t exist it’s created. Arrays are created for\n * missing index properties while objects are created for all other missing properties. Use _.setWith to\n * customize path creation.\n *\n * @param object The object to modify.\n * @param path The path of the property to set.\n * @param value The value to set.\n * @return Returns object.\n */\n set(\n object: Object,\n path: Many,\n value: any\n ): TResult;\n\n /**\n * @see _.set\n */\n set(\n object: Object,\n path: Many,\n value: V\n ): TResult;\n\n /**\n * @see _.set\n */\n set(\n object: O,\n path: Many,\n value: V\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.set\n */\n set(\n path: Many,\n value: any\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.set\n */\n set(\n path: Many,\n value: V\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.set\n */\n set(\n path: Many,\n value: any\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.set\n */\n set(\n path: Many,\n value: V\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.setWith\n type SetWithCustomizer = (nsValue: any, key: string, nsObject: T) => any;\n\n interface LoDashStatic {\n /**\n * This method is like _.set except that it accepts customizer which is invoked to produce the objects of\n * path. If customizer returns undefined path creation is handled by the method instead. The customizer is\n * invoked with three arguments: (nsValue, key, nsObject).\n *\n * @param object The object to modify.\n * @param path The path of the property to set.\n * @param value The value to set.\n * @parem customizer The function to customize assigned values.\n * @return Returns object.\n */\n setWith(\n object: Object,\n path: Many,\n value: any,\n customizer?: SetWithCustomizer\n ): TResult;\n\n /**\n * @see _.setWith\n */\n setWith(\n object: Object,\n path: Many,\n value: V,\n customizer?: SetWithCustomizer\n ): TResult;\n\n /**\n * @see _.setWith\n */\n setWith(\n object: O,\n path: Many,\n value: V,\n customizer?: SetWithCustomizer\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.setWith\n */\n setWith(\n path: Many,\n value: any,\n customizer?: SetWithCustomizer\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.setWith\n */\n setWith(\n path: Many,\n value: V,\n customizer?: SetWithCustomizer\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.setWith\n */\n setWith(\n path: Many,\n value: any,\n customizer?: SetWithCustomizer\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.setWith\n */\n setWith(\n path: Many,\n value: V,\n customizer?: SetWithCustomizer\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.toPairs\n interface LoDashStatic {\n /**\n * Creates an array of own enumerable key-value pairs for object.\n *\n * @param object The object to query.\n * @return Returns the new array of key-value pairs.\n */\n toPairs(object?: T): [string, any][];\n\n toPairs(object?: T): [string, TResult][];\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.toPairs\n */\n toPairs(): LoDashImplicitArrayWrapper<[string, TResult]>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.toPairs\n */\n toPairs(): LoDashExplicitArrayWrapper<[string, TResult]>;\n }\n\n //_.toPairsIn\n interface LoDashStatic {\n /**\n * Creates an array of own and inherited enumerable key-value pairs for object.\n *\n * @param object The object to query.\n * @return Returns the new array of key-value pairs.\n */\n toPairsIn(object?: T): [string, any][];\n\n toPairsIn(object?: T): [string, TResult][];\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.toPairsIn\n */\n toPairsIn(): LoDashImplicitArrayWrapper<[string, TResult]>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.toPairsIn\n */\n toPairsIn(): LoDashExplicitArrayWrapper<[string, TResult]>;\n }\n\n //_.transform\n interface LoDashStatic {\n /**\n * An alternative to _.reduce; this method transforms object to a new accumulator object which is the result of\n * running each of its own enumerable properties through iteratee, with each invocation potentially mutating\n * the accumulator object. The iteratee is bound to thisArg and invoked with four arguments: (accumulator,\n * value, key, object). Iteratee functions may exit iteration early by explicitly returning false.\n *\n * @param object The object to iterate over.\n * @param iteratee The function invoked per iteration.\n * @param accumulator The custom accumulator value.\n * @param thisArg The this binding of iteratee.\n * @return Returns the accumulated value.\n */\n transform(\n object: T[],\n iteratee?: MemoVoidArrayIterator,\n accumulator?: TResult[]\n ): TResult[];\n\n /**\n * @see _.transform\n */\n transform(\n object: T[],\n iteratee?: MemoVoidArrayIterator>,\n accumulator?: Dictionary\n ): Dictionary;\n\n /**\n * @see _.transform\n */\n transform(\n object: Dictionary,\n iteratee?: MemoVoidDictionaryIterator>,\n accumulator?: Dictionary\n ): Dictionary;\n\n /**\n * @see _.transform\n */\n transform(\n object: Dictionary,\n iteratee?: MemoVoidDictionaryIterator,\n accumulator?: TResult[]\n ): TResult[];\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.transform\n */\n transform(\n iteratee?: MemoVoidArrayIterator,\n accumulator?: TResult[]\n ): LoDashImplicitArrayWrapper;\n\n /**\n * @see _.transform\n */\n transform(\n iteratee?: MemoVoidArrayIterator>,\n accumulator?: Dictionary\n ): LoDashImplicitObjectWrapper>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.transform\n */\n transform(\n iteratee?: MemoVoidDictionaryIterator>,\n accumulator?: Dictionary\n ): LoDashImplicitObjectWrapper>;\n\n /**\n * @see _.transform\n */\n transform(\n iteratee?: MemoVoidDictionaryIterator,\n accumulator?: TResult[]\n ): LoDashImplicitArrayWrapper;\n }\n\n //_.unset\n interface LoDashStatic {\n /**\n * Removes the property at path of object.\n *\n * Note: This method mutates object.\n *\n * @param object The object to modify.\n * @param path The path of the property to unset.\n * @return Returns true if the property is deleted, else false.\n */\n unset(\n object: T,\n path: Many\n ): boolean;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.unset\n */\n unset(path: Many): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.unset\n */\n unset(path: Many): LoDashExplicitWrapper;\n }\n\n //_.update\n interface LoDashStatic {\n /**\n * This method is like _.set except that accepts updater to produce the value to set. Use _.updateWith to\n * customize path creation. The updater is invoked with one argument: (value).\n *\n * @param object The object to modify.\n * @param path The path of the property to set.\n * @param updater The function to produce the updated value.\n * @return Returns object.\n */\n update(\n object: Object,\n path: Many,\n updater: Function\n ): TResult;\n\n /**\n * @see _.update\n */\n update(\n object: Object,\n path: Many,\n updater: U\n ): TResult;\n\n /**\n * @see _.update\n */\n update(\n object: O,\n path: Many,\n updater: Function\n ): TResult;\n\n /**\n * @see _.update\n */\n update(\n object: O,\n path: Many,\n updater: U\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.update\n */\n update(\n path: Many,\n updater: any\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.update\n */\n update(\n path: Many,\n updater: U\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.update\n */\n update(\n path: Many,\n updater: any\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.update\n */\n update(\n path: Many,\n updater: U\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.values\n interface LoDashStatic {\n /**\n * Creates an array of the own enumerable property values of object.\n *\n * @param object The object to query.\n * @return Returns an array of property values.\n */\n values(object?: Dictionary|NumericDictionary|List | null | undefined): T[];\n\n /**\n * @see _.values\n */\n values(object?: any): T[];\n }\n\n interface LoDashImplicitStringWrapper {\n /**\n * @see _.values\n */\n values(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.values\n */\n values(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.values\n */\n values(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.values\n */\n values(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.values\n */\n values(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.values\n */\n values(): LoDashExplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.values\n */\n values(): LoDashExplicitArrayWrapper;\n }\n\n //_.valuesIn\n interface LoDashStatic {\n /**\n * Creates an array of the own and inherited enumerable property values of object.\n *\n * @param object The object to query.\n * @return Returns the array of property values.\n */\n valuesIn(object?: Dictionary): T[];\n\n /**\n * @see _.valuesIn\n */\n valuesIn(object?: any): T[];\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.valuesIn\n */\n valuesIn(): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.valuesIn\n */\n valuesIn(): LoDashExplicitArrayWrapper;\n }\n\n /**********\n * String *\n **********/\n\n //_.camelCase\n interface LoDashStatic {\n /**\n * Converts string to camel case.\n *\n * @param string The string to convert.\n * @return Returns the camel cased string.\n */\n camelCase(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.camelCase\n */\n camelCase(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.camelCase\n */\n camelCase(): LoDashExplicitWrapper;\n }\n\n //_.capitalize\n interface LoDashStatic {\n /**\n * Converts the first character of string to upper case and the remaining to lower case.\n *\n * @param string The string to capitalize.\n * @return Returns the capitalized string.\n */\n capitalize(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.capitalize\n */\n capitalize(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.capitalize\n */\n capitalize(): LoDashExplicitWrapper;\n }\n\n //_.deburr\n interface LoDashStatic {\n /**\n * Deburrs string by converting latin-1 supplementary letters to basic latin letters and removing combining\n * diacritical marks.\n *\n * @param string The string to deburr.\n * @return Returns the deburred string.\n */\n deburr(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.deburr\n */\n deburr(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.deburr\n */\n deburr(): LoDashExplicitWrapper;\n }\n\n //_.endsWith\n interface LoDashStatic {\n /**\n * Checks if string ends with the given target string.\n *\n * @param string The string to search.\n * @param target The string to search for.\n * @param position The position to search from.\n * @return Returns true if string ends with target, else false.\n */\n endsWith(\n string?: string,\n target?: string,\n position?: number\n ): boolean;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.endsWith\n */\n endsWith(\n target?: string,\n position?: number\n ): boolean;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.endsWith\n */\n endsWith(\n target?: string,\n position?: number\n ): LoDashExplicitWrapper;\n }\n\n // _.escape\n interface LoDashStatic {\n /**\n * Converts the characters \"&\", \"<\", \">\", '\"', \"'\", and \"`\" in string to their corresponding HTML entities.\n *\n * Note: No other characters are escaped. To escape additional characters use a third-party library like he.\n *\n * hough the \">\" character is escaped for symmetry, characters like \">\" and \"/\" don’t need escaping in HTML\n * and have no special meaning unless they're part of a tag or unquoted attribute value. See Mathias Bynens’s\n * article (under \"semi-related fun fact\") for more details.\n *\n * Backticks are escaped because in IE < 9, they can break out of attribute values or HTML comments. See #59,\n * #102, #108, and #133 of the HTML5 Security Cheatsheet for more details.\n *\n * When working with HTML you should always quote attribute values to reduce XSS vectors.\n *\n * @param string The string to escape.\n * @return Returns the escaped string.\n */\n escape(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.escape\n */\n escape(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.escape\n */\n escape(): LoDashExplicitWrapper;\n }\n\n // _.escapeRegExp\n interface LoDashStatic {\n /**\n * Escapes the RegExp special characters \"^\", \"$\", \"\\\", \".\", \"*\", \"+\", \"?\", \"(\", \")\", \"[\", \"]\",\n * \"{\", \"}\", and \"|\" in string.\n *\n * @param string The string to escape.\n * @return Returns the escaped string.\n */\n escapeRegExp(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.escapeRegExp\n */\n escapeRegExp(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.escapeRegExp\n */\n escapeRegExp(): LoDashExplicitWrapper;\n }\n\n //_.kebabCase\n interface LoDashStatic {\n /**\n * Converts string to kebab case.\n *\n * @param string The string to convert.\n * @return Returns the kebab cased string.\n */\n kebabCase(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.kebabCase\n */\n kebabCase(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.kebabCase\n */\n kebabCase(): LoDashExplicitWrapper;\n }\n\n //_.lowerCase\n interface LoDashStatic {\n /**\n * Converts `string`, as space separated words, to lower case.\n *\n * @param string The string to convert.\n * @return Returns the lower cased string.\n */\n lowerCase(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.lowerCase\n */\n lowerCase(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.lowerCase\n */\n lowerCase(): LoDashExplicitWrapper;\n }\n\n //_.lowerFirst\n interface LoDashStatic {\n /**\n * Converts the first character of `string` to lower case.\n *\n * @param string The string to convert.\n * @return Returns the converted string.\n */\n lowerFirst(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.lowerFirst\n */\n lowerFirst(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.lowerFirst\n */\n lowerFirst(): LoDashExplicitWrapper;\n }\n\n //_.pad\n interface LoDashStatic {\n /**\n * Pads string on the left and right sides if it’s shorter than length. Padding characters are truncated if\n * they can’t be evenly divided by length.\n *\n * @param string The string to pad.\n * @param length The padding length.\n * @param chars The string used as padding.\n * @return Returns the padded string.\n */\n pad(\n string?: string,\n length?: number,\n chars?: string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.pad\n */\n pad(\n length?: number,\n chars?: string\n ): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.pad\n */\n pad(\n length?: number,\n chars?: string\n ): LoDashExplicitWrapper;\n }\n\n //_.padEnd\n interface LoDashStatic {\n /**\n * Pads string on the right side if it’s shorter than length. Padding characters are truncated if they exceed\n * length.\n *\n * @param string The string to pad.\n * @param length The padding length.\n * @param chars The string used as padding.\n * @return Returns the padded string.\n */\n padEnd(\n string?: string,\n length?: number,\n chars?: string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.padEnd\n */\n padEnd(\n length?: number,\n chars?: string\n ): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.padEnd\n */\n padEnd(\n length?: number,\n chars?: string\n ): LoDashExplicitWrapper;\n }\n\n //_.padStart\n interface LoDashStatic {\n /**\n * Pads string on the left side if it’s shorter than length. Padding characters are truncated if they exceed\n * length.\n *\n * @param string The string to pad.\n * @param length The padding length.\n * @param chars The string used as padding.\n * @return Returns the padded string.\n */\n padStart(\n string?: string,\n length?: number,\n chars?: string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.padStart\n */\n padStart(\n length?: number,\n chars?: string\n ): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.padStart\n */\n padStart(\n length?: number,\n chars?: string\n ): LoDashExplicitWrapper;\n }\n\n //_.parseInt\n interface LoDashStatic {\n /**\n * Converts string to an integer of the specified radix. If radix is undefined or 0, a radix of 10 is used\n * unless value is a hexadecimal, in which case a radix of 16 is used.\n *\n * Note: This method aligns with the ES5 implementation of parseInt.\n *\n * @param string The string to convert.\n * @param radix The radix to interpret value by.\n * @return Returns the converted integer.\n */\n parseInt(\n string: string,\n radix?: number\n ): number;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.parseInt\n */\n parseInt(radix?: number): number;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.parseInt\n */\n parseInt(radix?: number): LoDashExplicitWrapper;\n }\n\n //_.repeat\n interface LoDashStatic {\n /**\n * Repeats the given string n times.\n *\n * @param string The string to repeat.\n * @param n The number of times to repeat the string.\n * @return Returns the repeated string.\n */\n repeat(\n string?: string,\n n?: number\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.repeat\n */\n repeat(n?: number): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.repeat\n */\n repeat(n?: number): LoDashExplicitWrapper;\n }\n\n //_.replace\n interface LoDashStatic {\n /**\n * Replaces matches for pattern in string with replacement.\n *\n * Note: This method is based on String#replace.\n *\n * @param string\n * @param pattern\n * @param replacement\n * @return Returns the modified string.\n */\n replace(\n string: string,\n pattern: RegExp|string,\n replacement: Function|string\n ): string;\n\n /**\n * @see _.replace\n */\n replace(\n pattern?: RegExp|string,\n replacement?: Function|string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.replace\n */\n replace(\n pattern?: RegExp|string,\n replacement?: Function|string\n ): string;\n\n /**\n * @see _.replace\n */\n replace(\n replacement?: Function|string\n ): string;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.replace\n */\n replace(\n pattern?: RegExp|string,\n replacement?: Function|string\n ): string;\n\n /**\n * @see _.replace\n */\n replace(\n replacement?: Function|string\n ): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.replace\n */\n replace(\n pattern?: RegExp|string,\n replacement?: Function|string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.replace\n */\n replace(\n replacement?: Function|string\n ): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.replace\n */\n replace(\n pattern?: RegExp|string,\n replacement?: Function|string\n ): LoDashExplicitWrapper;\n\n /**\n * @see _.replace\n */\n replace(\n replacement?: Function|string\n ): LoDashExplicitWrapper;\n }\n\n //_.snakeCase\n interface LoDashStatic {\n /**\n * Converts string to snake case.\n *\n * @param string The string to convert.\n * @return Returns the snake cased string.\n */\n snakeCase(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.snakeCase\n */\n snakeCase(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.snakeCase\n */\n snakeCase(): LoDashExplicitWrapper;\n }\n\n //_.split\n interface LoDashStatic {\n /**\n * Splits string by separator.\n *\n * Note: This method is based on String#split.\n *\n * @param string\n * @param separator\n * @param limit\n * @return Returns the new array of string segments.\n */\n split(\n string: string,\n separator?: RegExp|string,\n limit?: number\n ): string[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.split\n */\n split(\n separator?: RegExp|string,\n limit?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.split\n */\n split(\n separator?: RegExp|string,\n limit?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.startCase\n interface LoDashStatic {\n /**\n * Converts string to start case.\n *\n * @param string The string to convert.\n * @return Returns the start cased string.\n */\n startCase(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.startCase\n */\n startCase(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.startCase\n */\n startCase(): LoDashExplicitWrapper;\n }\n\n //_.startsWith\n interface LoDashStatic {\n /**\n * Checks if string starts with the given target string.\n *\n * @param string The string to search.\n * @param target The string to search for.\n * @param position The position to search from.\n * @return Returns true if string starts with target, else false.\n */\n startsWith(\n string?: string,\n target?: string,\n position?: number\n ): boolean;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.startsWith\n */\n startsWith(\n target?: string,\n position?: number\n ): boolean;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.startsWith\n */\n startsWith(\n target?: string,\n position?: number\n ): LoDashExplicitWrapper;\n }\n\n //_.template\n interface TemplateOptions extends TemplateSettings {\n /**\n * The sourceURL of the template's compiled source.\n */\n sourceURL?: string;\n }\n\n interface TemplateExecutor {\n (data?: Object): string;\n source: string;\n }\n\n interface LoDashStatic {\n /**\n * Creates a compiled template function that can interpolate data properties in \"interpolate\" delimiters,\n * HTML-escape interpolated data properties in \"escape\" delimiters, and execute JavaScript in \"evaluate\"\n * delimiters. Data properties may be accessed as free variables in the template. If a setting object is\n * provided it takes precedence over _.templateSettings values.\n *\n * Note: In the development build _.template utilizes\n * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) for easier\n * debugging.\n *\n * For more information on precompiling templates see\n * [lodash's custom builds documentation](https://lodash.com/custom-builds).\n *\n * For more information on Chrome extension sandboxes see\n * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).\n *\n * @param string The template string.\n * @param options The options object.\n * @param options.escape The HTML \"escape\" delimiter.\n * @param options.evaluate The \"evaluate\" delimiter.\n * @param options.imports An object to import into the template as free variables.\n * @param options.interpolate The \"interpolate\" delimiter.\n * @param options.sourceURL The sourceURL of the template's compiled source.\n * @param options.variable The data object variable name.\n * @return Returns the compiled template function.\n */\n template(\n string: string,\n options?: TemplateOptions\n ): TemplateExecutor;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.template\n */\n template(options?: TemplateOptions): TemplateExecutor;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.template\n */\n template(options?: TemplateOptions): LoDashExplicitObjectWrapper;\n }\n\n //_.toLower\n interface LoDashStatic {\n /**\n * Converts `string`, as a whole, to lower case.\n *\n * @param string The string to convert.\n * @return Returns the lower cased string.\n */\n toLower(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.toLower\n */\n toLower(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.toLower\n */\n toLower(): LoDashExplicitWrapper;\n }\n\n //_.toUpper\n interface LoDashStatic {\n /**\n * Converts `string`, as a whole, to upper case.\n *\n * @param string The string to convert.\n * @return Returns the upper cased string.\n */\n toUpper(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.toUpper\n */\n toUpper(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.toUpper\n */\n toUpper(): LoDashExplicitWrapper;\n }\n\n //_.trim\n interface LoDashStatic {\n /**\n * Removes leading and trailing whitespace or specified characters from string.\n *\n * @param string The string to trim.\n * @param chars The characters to trim.\n * @return Returns the trimmed string.\n */\n trim(\n string?: string,\n chars?: string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.trim\n */\n trim(chars?: string): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.trim\n */\n trim(chars?: string): LoDashExplicitWrapper;\n }\n\n //_.trimEnd\n interface LoDashStatic {\n /**\n * Removes trailing whitespace or specified characters from string.\n *\n * @param string The string to trim.\n * @param chars The characters to trim.\n * @return Returns the trimmed string.\n */\n trimEnd(\n string?: string,\n chars?: string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.trimEnd\n */\n trimEnd(chars?: string): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.trimEnd\n */\n trimEnd(chars?: string): LoDashExplicitWrapper;\n }\n\n //_.trimStart\n interface LoDashStatic {\n /**\n * Removes leading whitespace or specified characters from string.\n *\n * @param string The string to trim.\n * @param chars The characters to trim.\n * @return Returns the trimmed string.\n */\n trimStart(\n string?: string,\n chars?: string\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.trimStart\n */\n trimStart(chars?: string): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.trimStart\n */\n trimStart(chars?: string): LoDashExplicitWrapper;\n }\n\n //_.truncate\n interface TruncateOptions {\n /** The maximum string length. */\n length?: number;\n /** The string to indicate text is omitted. */\n omission?: string;\n /** The separator pattern to truncate to. */\n separator?: string|RegExp;\n }\n\n interface LoDashStatic {\n /**\n * Truncates string if it’s longer than the given maximum string length. The last characters of the truncated\n * string are replaced with the omission string which defaults to \"…\".\n *\n * @param string The string to truncate.\n * @param options The options object or maximum string length.\n * @return Returns the truncated string.\n */\n truncate(\n string?: string,\n options?: TruncateOptions\n ): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.truncate\n */\n truncate(options?: TruncateOptions): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.truncate\n */\n truncate(options?: TruncateOptions): LoDashExplicitWrapper;\n }\n\n //_.unescape\n interface LoDashStatic {\n /**\n * The inverse of _.escape; this method converts the HTML entities &, <, >, ", ', and `\n * in string to their corresponding characters.\n *\n * Note: No other HTML entities are unescaped. To unescape additional HTML entities use a third-party library\n * like he.\n *\n * @param string The string to unescape.\n * @return Returns the unescaped string.\n */\n unescape(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.unescape\n */\n unescape(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.unescape\n */\n unescape(): LoDashExplicitWrapper;\n }\n\n //_.upperCase\n interface LoDashStatic {\n /**\n * Converts `string`, as space separated words, to upper case.\n *\n * @param string The string to convert.\n * @return Returns the upper cased string.\n */\n upperCase(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.upperCase\n */\n upperCase(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.upperCase\n */\n upperCase(): LoDashExplicitWrapper;\n }\n\n //_.upperFirst\n interface LoDashStatic {\n /**\n * Converts the first character of `string` to upper case.\n *\n * @param string The string to convert.\n * @return Returns the converted string.\n */\n upperFirst(string?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.upperFirst\n */\n upperFirst(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.upperFirst\n */\n upperFirst(): LoDashExplicitWrapper;\n }\n\n //_.words\n interface LoDashStatic {\n /**\n * Splits `string` into an array of its words.\n *\n * @param string The string to inspect.\n * @param pattern The pattern to match words.\n * @return Returns the words of `string`.\n */\n words(\n string?: string,\n pattern?: string|RegExp\n ): string[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.words\n */\n words(pattern?: string|RegExp): string[];\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.words\n */\n words(pattern?: string|RegExp): LoDashExplicitArrayWrapper;\n }\n\n /***********\n * Utility *\n ***********/\n\n //_.attempt\n interface LoDashStatic {\n /**\n * Attempts to invoke func, returning either the result or the caught error object. Any additional arguments\n * are provided to func when it’s invoked.\n *\n * @param func The function to attempt.\n * @return Returns the func result or error object.\n */\n attempt(func: (...args: any[]) => TResult, ...args: any[]): TResult|Error;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.attempt\n */\n attempt(...args: any[]): TResult|Error;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.attempt\n */\n attempt(...args: any[]): LoDashExplicitObjectWrapper;\n }\n\n //_.constant\n interface LoDashStatic {\n /**\n * Creates a function that returns value.\n *\n * @param value The value to return from the new function.\n * @return Returns the new function.\n */\n constant(value: T): () => T;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.constant\n */\n constant(): LoDashImplicitObjectWrapper<() => TResult>;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.constant\n */\n constant(): LoDashExplicitObjectWrapper<() => TResult>;\n }\n\n //_.defaultTo\n interface LoDashStatic {\n /**\n * Checks `value` to determine whether a default value should be returned in\n * its place. The `defaultValue` is returned if `value` is `NaN`, `null`,\n * or `undefined`.\n *\n * @param value The value to check.\n * @param defaultValue The default value.\n * @returns Returns the resolved value.\n */\n defaultTo(value: T | null | undefined, defaultValue: T): T;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.defaultTo\n */\n defaultTo(value: TResult): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.defaultTo\n */\n defaultTo(value: TResult): LoDashExplicitObjectWrapper;\n }\n\n //_.identity\n interface LoDashStatic {\n /**\n * This method returns the first argument provided to it.\n *\n * @param value Any value.\n * @return Returns value.\n */\n identity(value: T): T;\n\n /**\n * @see _.identity\n */\n identity(): undefined;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.identity\n */\n identity(): T;\n }\n\n interface LoDashImplicitArrayWrapperBase {\n /**\n * @see _.identity\n */\n identity(): TArray;\n }\n\n interface LoDashImplicitObjectWrapperBase {\n /**\n * @see _.identity\n */\n identity(): TObject;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.identity\n */\n identity(): LoDashExplicitWrapper;\n }\n\n interface LoDashExplicitArrayWrapperBase {\n /**\n * @see _.identity\n */\n identity(): TWrapper;\n }\n\n interface LoDashExplicitObjectWrapperBase {\n /**\n * @see _.identity\n */\n identity(): TWrapper;\n }\n\n //_.iteratee\n interface LoDashStatic {\n /**\n * Creates a function that invokes `func` with the arguments of the created\n * function. If `func` is a property name the created callback returns the\n * property value for a given element. If `func` is an object the created\n * callback returns `true` for elements that contain the equivalent object properties, otherwise it returns `false`.\n *\n * @static\n * @memberOf _\n * @category Util\n * @param {*} [func=_.identity] The value to convert to a callback.\n * @returns {Function} Returns the callback.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 }\n * ];\n *\n * // create custom iteratee shorthands\n * _.iteratee = _.wrap(_.iteratee, function(callback, func) {\n * var p = /^(\\S+)\\s*([<>])\\s*(\\S+)$/.exec(func);\n * return !p ? callback(func) : function(object) {\n * return (p[2] == '>' ? object[p[1]] > p[3] : object[p[1]] < p[3]);\n * };\n * });\n *\n * _.filter(users, 'age > 36');\n * // => [{ 'user': 'fred', 'age': 40 }]\n */\n iteratee(\n func: TFunction\n ): TFunction;\n\n /**\n * @see _.iteratee\n */\n iteratee(\n func: string\n ): (object: any) => TResult;\n\n /**\n * @see _.iteratee\n */\n iteratee(\n func: Object\n ): (object: any) => boolean;\n\n /**\n * @see _.iteratee\n */\n iteratee(): (value: TResult) => TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.iteratee\n */\n iteratee(): LoDashImplicitObjectWrapper<(object: any) => TResult>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.iteratee\n */\n iteratee(): LoDashImplicitObjectWrapper<(object: any) => boolean>;\n\n /**\n * @see _.iteratee\n */\n iteratee(): LoDashImplicitObjectWrapper<(...args: any[]) => TResult>;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.iteratee\n */\n iteratee(): LoDashExplicitObjectWrapper<(object: any) => TResult>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.iteratee\n */\n iteratee(): LoDashExplicitObjectWrapper<(object: any) => boolean>;\n\n /**\n * @see _.iteratee\n */\n iteratee(): LoDashExplicitObjectWrapper<(...args: any[]) => TResult>;\n }\n\n //_.matches\n interface LoDashStatic {\n /**\n * Creates a function that performs a deep comparison between a given object and source, returning true if the\n * given object has equivalent property values, else false.\n *\n * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and\n * strings. Objects are compared by their own, not inherited, enumerable properties. For comparing a single own\n * or inherited property value see _.matchesProperty.\n *\n * @param source The object of property values to match.\n * @return Returns the new function.\n */\n matches(source: T): (value: any) => boolean;\n\n /**\n * @see _.matches\n */\n matches(source: T): (value: V) => boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.matches\n */\n matches(): LoDashImplicitObjectWrapper<(value: V) => boolean>;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.matches\n */\n matches(): LoDashExplicitObjectWrapper<(value: V) => boolean>;\n }\n\n //_.matchesProperty\n interface LoDashStatic {\n /**\n * Creates a function that compares the property value of path on a given object to value.\n *\n * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and\n * strings. Objects are compared by their own, not inherited, enumerable properties.\n *\n * @param path The path of the property to get.\n * @param srcValue The value to match.\n * @return Returns the new function.\n */\n matchesProperty(\n path: Many,\n srcValue: T\n ): (value: any) => boolean;\n\n /**\n * @see _.matchesProperty\n */\n matchesProperty(\n path: Many,\n srcValue: T\n ): (value: V) => boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.matchesProperty\n */\n matchesProperty(\n srcValue: SrcValue\n ): LoDashImplicitObjectWrapper<(value: any) => boolean>;\n\n /**\n * @see _.matchesProperty\n */\n matchesProperty(\n srcValue: SrcValue\n ): LoDashImplicitObjectWrapper<(value: Value) => boolean>;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.matchesProperty\n */\n matchesProperty(\n srcValue: SrcValue\n ): LoDashExplicitObjectWrapper<(value: any) => boolean>;\n\n /**\n * @see _.matchesProperty\n */\n matchesProperty(\n srcValue: SrcValue\n ): LoDashExplicitObjectWrapper<(value: Value) => boolean>;\n }\n\n //_.method\n interface LoDashStatic {\n /**\n * Creates a function that invokes the method at path on a given object. Any additional arguments are provided\n * to the invoked method.\n *\n * @param path The path of the method to invoke.\n * @param args The arguments to invoke the method with.\n * @return Returns the new function.\n */\n method(\n path: string|StringRepresentable[],\n ...args: any[]\n ): (object: TObject) => TResult;\n\n /**\n * @see _.method\n */\n method(\n path: string|StringRepresentable[],\n ...args: any[]\n ): (object: any) => TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashImplicitObjectWrapper<(object: TObject) => TResult>;\n\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashImplicitObjectWrapper<(object: any) => TResult>;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashImplicitObjectWrapper<(object: TObject) => TResult>;\n\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashImplicitObjectWrapper<(object: any) => TResult>;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashExplicitObjectWrapper<(object: TObject) => TResult>;\n\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashExplicitObjectWrapper<(object: any) => TResult>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashExplicitObjectWrapper<(object: TObject) => TResult>;\n\n /**\n * @see _.method\n */\n method(...args: any[]): LoDashExplicitObjectWrapper<(object: any) => TResult>;\n }\n\n //_.methodOf\n interface LoDashStatic {\n /**\n * The opposite of _.method; this method creates a function that invokes the method at a given path on object.\n * Any additional arguments are provided to the invoked method.\n *\n * @param object The object to query.\n * @param args The arguments to invoke the method with.\n * @return Returns the new function.\n */\n methodOf(\n object: TObject,\n ...args: any[]\n ): (path: Many) => TResult;\n\n /**\n * @see _.methodOf\n */\n methodOf(\n object: {},\n ...args: any[]\n ): (path: Many) => TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.methodOf\n */\n methodOf(\n ...args: any[]\n ): LoDashImplicitObjectWrapper<(path: Many) => TResult>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.methodOf\n */\n methodOf(\n ...args: any[]\n ): LoDashExplicitObjectWrapper<(path: Many) => TResult>;\n }\n\n //_.mixin\n interface MixinOptions {\n chain?: boolean;\n }\n\n interface LoDashStatic {\n /**\n * Adds all own enumerable function properties of a source object to the destination object. If object is a\n * function then methods are added to its prototype as well.\n *\n * Note: Use _.runInContext to create a pristine lodash function to avoid conflicts caused by modifying\n * the original.\n *\n * @param object The destination object.\n * @param source The object of functions to add.\n * @param options The options object.\n * @param options.chain Specify whether the functions added are chainable.\n * @return Returns object.\n */\n mixin(\n object: TObject,\n source: Dictionary,\n options?: MixinOptions\n ): TResult;\n\n /**\n * @see _.mixin\n */\n mixin(\n source: Dictionary,\n options?: MixinOptions\n ): TResult;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.mixin\n */\n mixin(\n source: Dictionary,\n options?: MixinOptions\n ): LoDashImplicitObjectWrapper;\n\n /**\n * @see _.mixin\n */\n mixin(\n options?: MixinOptions\n ): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.mixin\n */\n mixin(\n source: Dictionary,\n options?: MixinOptions\n ): LoDashExplicitObjectWrapper;\n\n /**\n * @see _.mixin\n */\n mixin(\n options?: MixinOptions\n ): LoDashExplicitObjectWrapper;\n }\n\n //_.noConflict\n interface LoDashStatic {\n /**\n * Reverts the _ variable to its previous value and returns a reference to the lodash function.\n *\n * @return Returns the lodash function.\n */\n noConflict(): typeof _;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.noConflict\n */\n noConflict(): typeof _;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.noConflict\n */\n noConflict(): LoDashExplicitObjectWrapper;\n }\n\n //_.noop\n interface LoDashStatic {\n /**\n * A no-operation function that returns undefined regardless of the arguments it receives.\n *\n * @return undefined\n */\n noop(...args: any[]): void;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.noop\n */\n noop(...args: any[]): void;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.noop\n */\n noop(...args: any[]): _.LoDashExplicitWrapper;\n }\n\n //_.nthArg\n interface LoDashStatic {\n /**\n * Creates a function that returns its nth argument.\n *\n * @param n The index of the argument to return.\n * @return Returns the new function.\n */\n nthArg(n?: number): TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.nthArg\n */\n nthArg(): LoDashImplicitObjectWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.nthArg\n */\n nthArg(): LoDashExplicitObjectWrapper;\n }\n\n //_.over\n interface LoDashStatic {\n /**\n * Creates a function that invokes iteratees with the arguments provided to the created function and returns\n * their results.\n *\n * @param iteratees The iteratees to invoke.\n * @return Returns the new function.\n */\n over(...iteratees: Array>): (...args: any[]) => TResult[];\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.over\n */\n over(...iteratees: Array>): LoDashImplicitObjectWrapper<(...args: any[]) => TResult[]>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.over\n */\n over(...iteratees: Array>): LoDashImplicitObjectWrapper<(...args: any[]) => TResult[]>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.over\n */\n over(...iteratees: Array>): LoDashExplicitObjectWrapper<(...args: any[]) => TResult[]>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.over\n */\n over(...iteratees: Array>): LoDashExplicitObjectWrapper<(...args: any[]) => TResult[]>;\n }\n\n //_.overEvery\n interface LoDashStatic {\n /**\n * Creates a function that checks if all of the predicates return truthy when invoked with the arguments\n * provided to the created function.\n *\n * @param predicates The predicates to check.\n * @return Returns the new function.\n */\n overEvery(...predicates: Array>): (...args: any[]) => boolean;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.overEvery\n */\n overEvery(...predicates: Array>): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.overEvery\n */\n overEvery(...predicates: Array>): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.overEvery\n */\n overEvery(...predicates: Array>): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.overEvery\n */\n overEvery(...predicates: Array>): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n //_.overSome\n interface LoDashStatic {\n /**\n * Creates a function that checks if any of the predicates return truthy when invoked with the arguments\n * provided to the created function.\n *\n * @param predicates The predicates to check.\n * @return Returns the new function.\n */\n overSome(...predicates: Array>): (...args: any[]) => boolean;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.overSome\n */\n overSome(...predicates: Array>): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.overSome\n */\n overSome(...predicates: Array>): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.overSome\n */\n overSome(...predicates: Array>): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.overSome\n */\n overSome(...predicates: Array>): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;\n }\n\n //_.property\n interface LoDashStatic {\n /**\n * Creates a function that returns the property value at path on a given object.\n *\n * @param path The path of the property to get.\n * @return Returns the new function.\n */\n property(path: Many): (obj: TObj) => TResult;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.property\n */\n property(): LoDashImplicitObjectWrapper<(obj: TObj) => TResult>;\n }\n\n interface LoDashImplicitArrayWrapper {\n /**\n * @see _.property\n */\n property(): LoDashImplicitObjectWrapper<(obj: TObj) => TResult>;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.property\n */\n property(): LoDashExplicitObjectWrapper<(obj: TObj) => TResult>;\n }\n\n interface LoDashExplicitArrayWrapper {\n /**\n * @see _.property\n */\n property(): LoDashExplicitObjectWrapper<(obj: TObj) => TResult>;\n }\n\n //_.propertyOf\n interface LoDashStatic {\n /**\n * The opposite of _.property; this method creates a function that returns the property value at a given path\n * on object.\n *\n * @param object The object to query.\n * @return Returns the new function.\n */\n propertyOf(object: T): (path: Many) => any;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.propertyOf\n */\n propertyOf(): LoDashImplicitObjectWrapper<(path: Many) => any>;\n }\n\n interface LoDashExplicitObjectWrapper {\n /**\n * @see _.propertyOf\n */\n propertyOf(): LoDashExplicitObjectWrapper<(path: Many) => any>;\n }\n\n //_.range\n interface LoDashStatic {\n /**\n * Creates an array of numbers (positive and/or negative) progressing from start up to, but not including, end.\n * If end is not specified it’s set to start with start then set to 0. If end is less than start a zero-length\n * range is created unless a negative step is specified.\n *\n * @param start The start of the range.\n * @param end The end of the range.\n * @param step The value to increment or decrement by.\n * @return Returns a new range array.\n */\n range(\n start: number,\n end: number,\n step?: number\n ): number[];\n\n /**\n * @see _.range\n */\n range(\n end: number,\n step?: number\n ): number[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.range\n */\n range(\n end?: number,\n step?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.range\n */\n range(\n end?: number,\n step?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.rangeRight\n interface LoDashStatic {\n /**\n * This method is like `_.range` except that it populates values in\n * descending order.\n *\n * @static\n * @memberOf _\n * @category Util\n * @param {number} [start=0] The start of the range.\n * @param {number} end The end of the range.\n * @param {number} [step=1] The value to increment or decrement by.\n * @returns {Array} Returns the new array of numbers.\n * @example\n *\n * _.rangeRight(4);\n * // => [3, 2, 1, 0]\n *\n * _.rangeRight(-4);\n * // => [-3, -2, -1, 0]\n *\n * _.rangeRight(1, 5);\n * // => [4, 3, 2, 1]\n *\n * _.rangeRight(0, 20, 5);\n * // => [15, 10, 5, 0]\n *\n * _.rangeRight(0, -4, -1);\n * // => [-3, -2, -1, 0]\n *\n * _.rangeRight(1, 4, 0);\n * // => [1, 1, 1]\n *\n * _.rangeRight(0);\n * // => []\n */\n rangeRight(\n start: number,\n end: number,\n step?: number\n ): number[];\n\n /**\n * @see _.rangeRight\n */\n rangeRight(\n end: number,\n step?: number\n ): number[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.rangeRight\n */\n rangeRight(\n end?: number,\n step?: number\n ): LoDashImplicitArrayWrapper;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.rangeRight\n */\n rangeRight(\n end?: number,\n step?: number\n ): LoDashExplicitArrayWrapper;\n }\n\n //_.runInContext\n interface LoDashStatic {\n /**\n * Create a new pristine lodash function using the given context object.\n *\n * @param context The context object.\n * @return Returns a new lodash function.\n */\n runInContext(context?: Object): typeof _;\n }\n\n interface LoDashImplicitObjectWrapper {\n /**\n * @see _.runInContext\n */\n runInContext(): typeof _;\n }\n\n // _.stubArray\n interface LoDashStatic {\n /**\n * This method returns a new empty array.\n *\n * @returns Returns the new empty array.\n */\n stubArray(): any[];\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.stubArray\n */\n stubArray(): any[];\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.stubArray\n */\n stubArray(): _.LoDashExplicitArrayWrapper;\n }\n\n // _.stubFalse\n interface LoDashStatic {\n /**\n * This method returns `false`.\n *\n * @returns Returns `false`.\n */\n stubFalse(): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.stubFalse\n */\n stubFalse(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.stubFalse\n */\n stubFalse(): _.LoDashExplicitWrapper;\n }\n\n interface LoDashStatic {\n /**\n * This method returns a new empty object.\n *\n * @returns Returns the new empty object.\n */\n stubObject(): Object;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.stubObject\n */\n stubObject(): Object;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.stubObject\n */\n stubObject(): _.LoDashExplicitObjectWrapper;\n }\n\n interface LoDashStatic {\n /**\n * This method returns an empty string.\n *\n * @returns Returns the empty string.\n */\n stubString(): string;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.stubString\n */\n stubString(): string;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.stubString\n */\n stubString(): _.LoDashExplicitWrapper;\n }\n\n interface LoDashStatic {\n /**\n * This method returns `true`.\n *\n * @returns Returns `true`.\n */\n stubTrue(): boolean;\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.stubTrue\n */\n stubTrue(): boolean;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.stubTrue\n */\n stubTrue(): _.LoDashExplicitWrapper;\n }\n\n //_.times\n interface LoDashStatic {\n /**\n * Invokes the iteratee function n times, returning an array of the results of each invocation. The iteratee\n * is invoked with one argument; (index).\n *\n * @param n The number of times to invoke iteratee.\n * @param iteratee The function invoked per iteration.\n * @return Returns the array of results.\n */\n times(\n n: number,\n iteratee: (num: number) => TResult\n ): TResult[];\n\n /**\n * @see _.times\n */\n times(n: number): number[];\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.times\n */\n times(\n iteratee: (num: number) => TResult\n ): TResult[];\n\n /**\n * @see _.times\n */\n times(): number[];\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.times\n */\n times(\n iteratee: (num: number) => TResult\n ): LoDashExplicitArrayWrapper;\n\n /**\n * @see _.times\n */\n times(): LoDashExplicitArrayWrapper;\n }\n\n //_.toPath\n interface LoDashStatic {\n /**\n * Converts `value` to a property path array.\n *\n * @static\n * @memberOf _\n * @category Util\n * @param {*} value The value to convert.\n * @returns {Array} Returns the new property path array.\n * @example\n *\n * _.toPath('a.b.c');\n * // => ['a', 'b', 'c']\n *\n * _.toPath('a[0].b.c');\n * // => ['a', '0', 'b', 'c']\n *\n * var path = ['a', 'b', 'c'],\n * newPath = _.toPath(path);\n *\n * console.log(newPath);\n * // => ['a', 'b', 'c']\n *\n * console.log(path === newPath);\n * // => false\n */\n toPath(value: any): string[];\n }\n\n interface LoDashImplicitWrapperBase {\n /**\n * @see _.toPath\n */\n toPath(): LoDashImplicitWrapper;\n }\n\n interface LoDashExplicitWrapperBase {\n /**\n * @see _.toPath\n */\n toPath(): LoDashExplicitWrapper;\n }\n\n //_.uniqueId\n interface LoDashStatic {\n /**\n * Generates a unique ID. If prefix is provided the ID is appended to it.\n *\n * @param prefix The value to prefix the ID with.\n * @return Returns the unique ID.\n */\n uniqueId(prefix?: string): string;\n }\n\n interface LoDashImplicitWrapper {\n /**\n * @see _.uniqueId\n */\n uniqueId(): string;\n }\n\n interface LoDashExplicitWrapper {\n /**\n * @see _.uniqueId\n */\n uniqueId(): LoDashExplicitWrapper;\n }\n\n type ListIterator = (value: T, index: number, collection: List) => TResult;\n\n type ListIteratorTypeGuard = (value: T, index: number, collection: List) => value is S;\n\n type DictionaryIterator = (value: T, key: string, collection: Dictionary) => TResult;\n\n type DictionaryIteratorTypeGuard = (value: T, key: string, collection: Dictionary) => value is S;\n\n type NumericDictionaryIterator = (value: T, key: number, collection: Dictionary) => TResult;\n\n type ObjectIterator = (element: T, key: string, collection: any) => TResult;\n\n type StringIterator = (char: string, index: number, string: string) => TResult;\n\n type MemoVoidIterator = (prev: TResult, curr: T, indexOrKey: any, list: T[]) => void;\n\n type MemoIterator = (prev: TResult, curr: T, indexOrKey: any, list: T[]) => TResult;\n\n type MemoVoidArrayIterator = (acc: TResult, curr: T, index: number, arr: T[]) => void;\n type MemoVoidDictionaryIterator = (acc: TResult, curr: T, key: string, dict: Dictionary) => void;\n\n /** Common interface between Arrays and jQuery objects */\n type List = ArrayLike;\n\n interface Dictionary {\n [index: string]: T;\n }\n\n interface NumericDictionary {\n [index: number]: T;\n }\n\n interface StringRepresentable {\n toString(): string;\n }\n\n interface Cancelable {\n cancel(): void;\n flush(): void;\n }\n}\n\n// Backward compatibility with --target es5\ndeclare global {\n interface Set { }\n interface Map { }\n interface WeakSet { }\n interface WeakMap { }\n}\n" } + ); + verifyAfterPartialOrCompleteNpmInstall(2); + + filesAndFoldersToAdd.push( + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/src/scheduler" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/src/util" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/symbol" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/testing" }, + { "path": "/a/b/node_modules/.staging/rxjs-22375c61/package.json.2252192041", "content": "{\n \"_args\": [\n [\n {\n \"raw\": \"rxjs@^5.4.2\",\n \"scope\": null,\n \"escapedName\": \"rxjs\",\n \"name\": \"rxjs\",\n \"rawSpec\": \"^5.4.2\",\n \"spec\": \">=5.4.2 <6.0.0\",\n \"type\": \"range\"\n },\n \"C:\\\\Users\\\\shkamat\\\\Desktop\\\\app\"\n ]\n ],\n \"_from\": \"rxjs@>=5.4.2 <6.0.0\",\n \"_id\": \"rxjs@5.4.3\",\n \"_inCache\": true,\n \"_location\": \"/rxjs\",\n \"_nodeVersion\": \"7.7.2\",\n \"_npmOperationalInternal\": {\n \"host\": \"s3://npm-registry-packages\",\n \"tmp\": \"tmp/rxjs-5.4.3.tgz_1502407898166_0.6800217325799167\"\n },\n \"_npmUser\": {\n \"name\": \"blesh\",\n \"email\": \"ben@benlesh.com\"\n },\n \"_npmVersion\": \"5.3.0\",\n \"_phantomChildren\": {},\n \"_requested\": {\n \"raw\": \"rxjs@^5.4.2\",\n \"scope\": null,\n \"escapedName\": \"rxjs\",\n \"name\": \"rxjs\",\n \"rawSpec\": \"^5.4.2\",\n \"spec\": \">=5.4.2 <6.0.0\",\n \"type\": \"range\"\n },\n \"_requiredBy\": [\n \"/\"\n ],\n \"_resolved\": \"https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz\",\n \"_shasum\": \"0758cddee6033d68e0fd53676f0f3596ce3d483f\",\n \"_shrinkwrap\": null,\n \"_spec\": \"rxjs@^5.4.2\",\n \"_where\": \"C:\\\\Users\\\\shkamat\\\\Desktop\\\\app\",\n \"author\": {\n \"name\": \"Ben Lesh\",\n \"email\": \"ben@benlesh.com\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/ReactiveX/RxJS/issues\"\n },\n \"config\": {\n \"commitizen\": {\n \"path\": \"cz-conventional-changelog\"\n }\n },\n \"contributors\": [\n {\n \"name\": \"Ben Lesh\",\n \"email\": \"ben@benlesh.com\"\n },\n {\n \"name\": \"Paul Taylor\",\n \"email\": \"paul.e.taylor@me.com\"\n },\n {\n \"name\": \"Jeff Cross\",\n \"email\": \"crossj@google.com\"\n },\n {\n \"name\": \"Matthew Podwysocki\",\n \"email\": \"matthewp@microsoft.com\"\n },\n {\n \"name\": \"OJ Kwon\",\n \"email\": \"kwon.ohjoong@gmail.com\"\n },\n {\n \"name\": \"Andre Staltz\",\n \"email\": \"andre@staltz.com\"\n }\n ],\n \"dependencies\": {\n \"symbol-observable\": \"^1.0.1\"\n },\n \"description\": \"Reactive Extensions for modern JavaScript\",\n \"devDependencies\": {\n \"babel-polyfill\": \"^6.23.0\",\n \"benchmark\": \"^2.1.0\",\n \"benchpress\": \"2.0.0-beta.1\",\n \"chai\": \"^3.5.0\",\n \"color\": \"^0.11.1\",\n \"colors\": \"1.1.2\",\n \"commitizen\": \"^2.8.6\",\n \"coveralls\": \"^2.11.13\",\n \"cz-conventional-changelog\": \"^1.2.0\",\n \"danger\": \"^1.1.0\",\n \"doctoc\": \"^1.0.0\",\n \"escape-string-regexp\": \"^1.0.5 \",\n \"esdoc\": \"^0.4.7\",\n \"eslint\": \"^3.8.0\",\n \"fs-extra\": \"^2.1.2\",\n \"get-folder-size\": \"^1.0.0\",\n \"glob\": \"^7.0.3\",\n \"gm\": \"^1.22.0\",\n \"google-closure-compiler-js\": \"^20170218.0.0\",\n \"gzip-size\": \"^3.0.0\",\n \"http-server\": \"^0.9.0\",\n \"husky\": \"^0.13.3\",\n \"lint-staged\": \"3.2.5\",\n \"lodash\": \"^4.15.0\",\n \"madge\": \"^1.4.3\",\n \"markdown-doctest\": \"^0.9.1\",\n \"minimist\": \"^1.2.0\",\n \"mkdirp\": \"^0.5.1\",\n \"mocha\": \"^3.0.2\",\n \"mocha-in-sauce\": \"0.0.1\",\n \"npm-run-all\": \"^4.0.2\",\n \"npm-scripts-info\": \"^0.3.4\",\n \"nyc\": \"^10.2.0\",\n \"opn-cli\": \"^3.1.0\",\n \"platform\": \"^1.3.1\",\n \"promise\": \"^7.1.1\",\n \"protractor\": \"^3.1.1\",\n \"rollup\": \"0.36.3\",\n \"rollup-plugin-inject\": \"^2.0.0\",\n \"rollup-plugin-node-resolve\": \"^2.0.0\",\n \"rx\": \"latest\",\n \"rxjs\": \"latest\",\n \"shx\": \"^0.2.2\",\n \"sinon\": \"^2.1.0\",\n \"sinon-chai\": \"^2.9.0\",\n \"source-map-support\": \"^0.4.0\",\n \"tslib\": \"^1.5.0\",\n \"tslint\": \"^4.4.2\",\n \"typescript\": \"~2.0.6\",\n \"typings\": \"^2.0.0\",\n \"validate-commit-msg\": \"^2.14.0\",\n \"watch\": \"^1.0.1\",\n \"webpack\": \"^1.13.1\",\n \"xmlhttprequest\": \"1.8.0\"\n },\n \"directories\": {},\n \"dist\": {\n \"integrity\": \"sha512-fSNi+y+P9ss+EZuV0GcIIqPUK07DEaMRUtLJvdcvMyFjc9dizuDjere+A4V7JrLGnm9iCc+nagV/4QdMTkqC4A==\",\n \"shasum\": \"0758cddee6033d68e0fd53676f0f3596ce3d483f\",\n \"tarball\": \"https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz\"\n },\n \"engines\": {\n \"npm\": \">=2.0.0\"\n },\n \"homepage\": \"https://github.com/ReactiveX/RxJS\",\n \"keywords\": [\n \"Rx\",\n \"RxJS\",\n \"ReactiveX\",\n \"ReactiveExtensions\",\n \"Streams\",\n \"Observables\",\n \"Observable\",\n \"Stream\",\n \"ES6\",\n \"ES2015\"\n ],\n \"license\": \"Apache-2.0\",\n \"lint-staged\": {\n \"*.@(js)\": [\n \"eslint --fix\",\n \"git add\"\n ],\n \"*.@(ts)\": [\n \"tslint --fix\",\n \"git add\"\n ]\n },\n \"main\": \"Rx.js\",\n \"maintainers\": [\n {\n \"name\": \"blesh\",\n \"email\": \"ben@benlesh.com\"\n }\n ],\n \"name\": \"rxjs\",\n \"optionalDependencies\": {},\n \"readme\": \"ERROR: No README data found!\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+ssh://git@github.com/ReactiveX/RxJS.git\"\n },\n \"scripts-info\": {\n \"info\": \"List available script\",\n \"build_all\": \"Build all packages (ES6, CJS, UMD) and generate packages\",\n \"build_cjs\": \"Build CJS package with clean up existing build, copy source into dist\",\n \"build_es6\": \"Build ES6 package with clean up existing build, copy source into dist\",\n \"build_closure_core\": \"Minify Global core build using closure compiler\",\n \"build_global\": \"Build Global package, then minify build\",\n \"build_perf\": \"Build CJS & Global build, run macro performance test\",\n \"build_test\": \"Build CJS package & test spec, execute mocha test runner\",\n \"build_cover\": \"Run lint to current code, build CJS & test spec, execute test coverage\",\n \"build_docs\": \"Build ES6 & global package, create documentation using it\",\n \"build_spec\": \"Build test specs\",\n \"check_circular_dependencies\": \"Check codebase has circular dependencies\",\n \"clean_spec\": \"Clean up existing test spec build output\",\n \"clean_dist_cjs\": \"Clean up existing CJS package output\",\n \"clean_dist_es6\": \"Clean up existing ES6 package output\",\n \"clean_dist_global\": \"Clean up existing Global package output\",\n \"commit\": \"Run git commit wizard\",\n \"compile_dist_cjs\": \"Compile codebase into CJS module\",\n \"compile_module_es6\": \"Compile codebase into ES6\",\n \"cover\": \"Execute test coverage\",\n \"lint_perf\": \"Run lint against performance test suite\",\n \"lint_spec\": \"Run lint against test spec\",\n \"lint_src\": \"Run lint against source\",\n \"lint\": \"Run lint against everything\",\n \"perf\": \"Run macro performance benchmark\",\n \"perf_micro\": \"Run micro performance benchmark\",\n \"test_mocha\": \"Execute mocha test runner against existing test spec build\",\n \"test_browser\": \"Execute mocha test runner on browser against existing test spec build\",\n \"test\": \"Clean up existing test spec build, build test spec and execute mocha test runner\",\n \"tests2png\": \"Generate marble diagram image from test spec\",\n \"watch\": \"Watch codebase, trigger compile when source code changes\"\n },\n \"typings\": \"Rx.d.ts\",\n \"version\": \"5.4.3\"\n}\n" } + ); + verifyAfterPartialOrCompleteNpmInstall(0); + + // remove /a/b/node_modules/.staging/rxjs-22375c61/package.json.2252192041 + filesAndFoldersToAdd.length--; + // and add few more folders/files + filesAndFoldersToAdd.push( + { "path": "/a/b/node_modules/symbol-observable" }, + { "path": "/a/b/node_modules/@types" }, + { "path": "/a/b/node_modules/@types/lodash" }, + { "path": "/a/b/node_modules/lodash" }, + { "path": "/a/b/node_modules/rxjs" }, + { "path": "/a/b/node_modules/typescript" }, + { "path": "/a/b/node_modules/.bin" } + ); + verifyAfterPartialOrCompleteNpmInstall(0); + + forEach(filesAndFoldersToAdd, f => { + f.path = f.path + .replace("/a/b/node_modules/.staging", "/a/b/node_modules") + .replace(/[\-\.][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w][\d\w]/g, ""); + }); + + const lodashIndexPath = "/a/b/node_modules/@types/lodash/index.d.ts"; + projectFiles.push(find(filesAndFoldersToAdd, f => f.path === lodashIndexPath)); + watchedModuleLocations.length = watchedModuleLocations.indexOf(lodashIndexPath); + // npm installation complete, timeout after reload fs + timeoutAfterReloadFs = true; + verifyAfterPartialOrCompleteNpmInstall(2); + + function verifyAfterPartialOrCompleteNpmInstall(timeoutQueueLengthWhenRunningTimeouts: number) { + host.reloadFS(projectFiles.concat(otherFiles, filesAndFoldersToAdd)); + if (timeoutAfterReloadFs) { + host.checkTimeoutQueueLengthAndRun(timeoutQueueLengthWhenRunningTimeouts); + } + else { + host.checkTimeoutQueueLength(2); + } + verifyProject(); + } + + function verifyProject() { + checkNumberOfConfiguredProjects(projectService, 1); + + const project = projectService.configuredProjects.get(tsconfigJson.path); + const projectFilePaths = map(projectFiles, f => f.path); + checkProjectActualFiles(project, projectFilePaths); + + const filesWatched = filter(projectFilePaths, p => p !== app.path).concat(watchedModuleLocations); + checkWatchedFiles(host, filesWatched); + checkWatchedDirectories(host, [appFolder], /*recursive*/ true); + checkWatchedDirectories(host, [], /*recursive*/ false); + } + + function getNodeModulesWatchedDirectories(path: string, module: string): string[] { + const nodeModulesDir = combinePaths(path, "node_modules/"); + const parentDir = getDirectoryPath(path); + const parentNodeModules = parentDir !== path ? getNodeModulesWatchedDirectories(parentDir, module) : []; + return [ + `${nodeModulesDir}${module}.ts`, + `${nodeModulesDir}${module}.tsx`, + `${nodeModulesDir}${module}.d.ts`, + `${nodeModulesDir}${module}/index.ts`, + `${nodeModulesDir}${module}/index.tsx`, + `${nodeModulesDir}${module}/index.d.ts`, + `${nodeModulesDir}@types/${module}.d.ts`, + `${nodeModulesDir}@types/${module}/index.d.ts`, + `${nodeModulesDir}@types/${module}/package.json`, + `${nodeModulesDir}${module}.js`, + `${nodeModulesDir}${module}.jsx`, + `${nodeModulesDir}${module}/package.json`, + `${nodeModulesDir}${module}/index.js`, + `${nodeModulesDir}${module}/index.jsx`, + ].concat(parentNodeModules); + } + } + + it("timeouts occur inbetween installation", () => { + verifyNpmInstall(/*timeoutDuringPartialInstallation*/ true); + }); + + it("timeout occurs after installation", () => { + verifyNpmInstall(/*timeoutDuringPartialInstallation*/ false); + }); + }); + }); + + describe("ProjectChangedEvent", () => { + function verifyFiles(caption: string, actual: ReadonlyArray, expected: ReadonlyArray) { + assert.equal(actual.length, expected.length, `Incorrect number of ${caption}. Actual: ${actual} Expected: ${expected}`); + const seen = createMap(); + forEach(actual, f => { + assert.isFalse(seen.has(f), `${caption}: Found duplicate ${f}. Actual: ${actual} Expected: ${expected}`); + seen.set(f, true); + assert.isTrue(contains(expected, f), `${caption}: Expected not to contain ${f}. Actual: ${actual} Expected: ${expected}`); + }); + } + + function createVerifyInitialOpen(session: TestSession, verifyProjectChangedEventHandler: (events: server.ProjectChangedEvent[]) => void) { + return (file: FileOrFolder) => { + session.executeCommandSeq({ + command: server.CommandNames.Open, + arguments: { + file: file.path + } + }); + verifyProjectChangedEventHandler([]); + }; + } + + interface ProjectChangeEventVerifier { + session: TestSession; + verifyProjectChangedEventHandler(events: server.ProjectChangedEvent[]): void; + verifyInitialOpen(file: FileOrFolder): void; + } + + function verifyProjectChangedEvent(createSession: (host: TestServerHost) => ProjectChangeEventVerifier) { + it("when adding new file", () => { + const commonFile1: FileOrFolder = { + path: "/a/b/file1.ts", + content: "export var x = 10;" + }; + const commonFile2: FileOrFolder = { + path: "/a/b/file2.ts", + content: "export var y = 10;" + }; + const commonFile3: FileOrFolder = { + path: "/a/b/file3.ts", + content: "export var z = 10;" + }; + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{}` + }; + const host = createServerHost([commonFile1, libFile, configFile]); + const { session, verifyProjectChangedEventHandler, verifyInitialOpen } = createSession(host, ); + const projectService = session.getProjectService(); + verifyInitialOpen(commonFile1); + + host.reloadFS([commonFile1, libFile, configFile, commonFile2]); + host.runQueuedTimeoutCallbacks(); + // Since this is first event + const project = projectService.configuredProjects.get(configFile.path); + verifyProjectChangedEventHandler([{ + eventName: server.ProjectChangedEvent, + data: { + project, + changedFiles: [libFile.path, commonFile1.path, commonFile2.path], + filesToEmit: [commonFile1.path, commonFile2.path] + } + }]); + + host.reloadFS([commonFile1, commonFile2, libFile, configFile, commonFile3]); + host.runQueuedTimeoutCallbacks(); + verifyProjectChangedEventHandler([{ + eventName: server.ProjectChangedEvent, + data: { + project, + changedFiles: [commonFile3.path], + filesToEmit: [commonFile3.path] + } + }]); + }); + + describe("with --out or --outFile setting", () => { + function verifyEventWithOutSettings(compilerOptions: CompilerOptions = {}) { + const config: FileOrFolder = { + path: "/a/tsconfig.json", + content: JSON.stringify({ + compilerOptions + }) + }; + + const f1: FileOrFolder = { + path: "/a/a.ts", + content: "export let x = 1" + }; + const f2: FileOrFolder = { + path: "/a/b.ts", + content: "export let y = 1" + }; + + const files = [f1, config, libFile]; + const host = createServerHost(files); + const { session, verifyInitialOpen, verifyProjectChangedEventHandler } = createSession(host); + const projectService = session.getProjectService(); + verifyInitialOpen(f1); + + files.push(f2); + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + + // Since this is first event + const project = projectService.configuredProjects.get(config.path); + verifyProjectChangedEventHandler([{ + eventName: server.ProjectChangedEvent, + data: { + project, + changedFiles: [libFile.path, f1.path, f2.path], + filesToEmit: [f1.path, f2.path] + } + }]); + + f2.content = "export let x = 11"; + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + verifyProjectChangedEventHandler([{ + eventName: server.ProjectChangedEvent, + data: { + project, + changedFiles: [f2.path], + filesToEmit: [f2.path] + } + }]); + } + + it("when both options are not set", () => { + verifyEventWithOutSettings(); + }); + + it("when --out is set", () => { + const outJs = "/a/out.js"; + verifyEventWithOutSettings({ out: outJs }); + }); + + it("when --outFile is set", () => { + const outJs = "/a/out.js"; + verifyEventWithOutSettings({ outFile: outJs }); + }); + }); + + describe("with modules and configured project", () => { + const file1Consumer1Path = "/a/b/file1Consumer1.ts"; + const moduleFile1Path = "/a/b/moduleFile1.ts"; + const configFilePath = "/a/b/tsconfig.json"; + type InitialStateParams = { + /** custom config file options */ + configObj?: any; + /** list of files emitted/changed on first update graph */ + firstCompilationEmitFiles?: string[]; + /** Additional files and folders to add */ + getAdditionalFileOrFolder?(): FileOrFolder[]; + /** initial list of files to reload in fs and first file in this list being the file to open */ + firstReloadFileList?: string[]; + }; + function getInitialState({ configObj = {}, getAdditionalFileOrFolder, firstReloadFileList, firstCompilationEmitFiles }: InitialStateParams = {}) { + const moduleFile1: FileOrFolder = { + path: moduleFile1Path, + content: "export function Foo() { };", + }; + + const file1Consumer1: FileOrFolder = { + path: file1Consumer1Path, + content: `import {Foo} from "./moduleFile1"; export var y = 10;`, + }; + + const file1Consumer2: FileOrFolder = { + path: "/a/b/file1Consumer2.ts", + content: `import {Foo} from "./moduleFile1"; let z = 10;`, + }; + + const moduleFile2: FileOrFolder = { + path: "/a/b/moduleFile2.ts", + content: `export var Foo4 = 10;`, + }; + + const globalFile3: FileOrFolder = { + path: "/a/b/globalFile3.ts", + content: `interface GlobalFoo { age: number }` + }; + + const additionalFiles = getAdditionalFileOrFolder ? getAdditionalFileOrFolder() : []; + const configFile = { + path: configFilePath, + content: JSON.stringify(configObj || { compilerOptions: {} }) + }; + + const files = [file1Consumer1, moduleFile1, file1Consumer2, moduleFile2, ...additionalFiles, globalFile3, libFile, configFile]; + + const filesToReload = firstReloadFileList && getFiles(firstReloadFileList) || files; + const host = createServerHost([filesToReload[0], configFile]); + + // Initial project creation + const { session, verifyProjectChangedEventHandler, verifyInitialOpen } = createSession(host); + const projectService = session.getProjectService(); + verifyInitialOpen(filesToReload[0]); + + // Since this is first event, it will have all the files + const firstFilesExpected = firstCompilationEmitFiles && getFiles(firstCompilationEmitFiles) || filesToReload; + verifyProjectChangedEvent(firstFilesExpected, filesToReload); + + return { + moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile, + files, + updateContentOfOpenFile, + verifyProjectChangedEvent, + verifyAffectedAllFiles, + }; + + function getFiles(filelist: string[]) { + return map(filelist, getFile); + } + + function getFile(fileName: string) { + return find(files, file => file.path === fileName); + } + + function verifyAffectedAllFiles() { + verifyProjectChangedEvent(filter(files, f => f !== libFile)); + } + + function verifyProjectChangedEvent(filesToEmit: FileOrFolder[], filesToReload?: FileOrFolder[], additionalChangedFiles?: FileOrFolder[]) { + const changedFiles = mapDefined(additionalChangedFiles ? filesToEmit.concat(additionalChangedFiles) : filesToEmit, f => f !== configFile ? f.path : undefined); + host.reloadFS(filesToReload || files); + host.runQueuedTimeoutCallbacks(); + const project = projectService.configuredProjects.get(configFile.path); + verifyProjectChangedEventHandler([{ + eventName: server.ProjectChangedEvent, + data: { + project, + changedFiles, + filesToEmit: mapDefined(filesToEmit, f => f !== libFile && f !== configFile ? f.path : undefined) + } + }]); + } + + function updateContentOfOpenFile(file: FileOrFolder, newContent: string) { + session.executeCommandSeq({ + command: server.CommandNames.Change, + arguments: { + file: file.path, + insertString: newContent, + endLine: 1, + endOffset: file.content.length, + line: 1, + offset: 1 + } + }); + file.content = newContent; + } + } + + it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => { + const { moduleFile1, file1Consumer1, file1Consumer2, verifyProjectChangedEvent } = getInitialState(); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer1, file1Consumer2]); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { console.log('hi'); };` + moduleFile1.content = `export var T: number;export function Foo() { console.log('hi'); };`; + verifyProjectChangedEvent([moduleFile1]); + }); + + it("should be up-to-date with the reference map changes", () => { + const { moduleFile1, file1Consumer1, file1Consumer2, updateContentOfOpenFile, verifyProjectChangedEvent } = getInitialState(); + + // Change file1Consumer1 content to `export let y = Foo();` + updateContentOfOpenFile(file1Consumer1, "export let y = Foo();"); + verifyProjectChangedEvent([file1Consumer1]); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer2]); + + // Add the import statements back to file1Consumer1 + updateContentOfOpenFile(file1Consumer1, `import {Foo} from "./moduleFile1";let y = Foo();`); + verifyProjectChangedEvent([file1Consumer1]); + + // Change the content of moduleFile1 to `export var T: number;export var T2: string;export function Foo() { };` + moduleFile1.content = `export var T: number;export var T2: string;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer2, file1Consumer1]); + + // Multiple file edits in one go: + + // Change file1Consumer1 content to `export let y = Foo();` + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + updateContentOfOpenFile(file1Consumer1, `export let y = Foo();`); + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer1, file1Consumer2]); + }); + + it("should be up-to-date with deleted files", () => { + const { moduleFile1, file1Consumer1, file1Consumer2, files, verifyProjectChangedEvent } = getInitialState(); + + // Change the content of moduleFile1 to `export var T: number;export function Foo() { };` + moduleFile1.content = `export var T: number;export function Foo() { };`; + + // Delete file1Consumer2 + const filesToLoad = filter(files, file => file !== file1Consumer2); + verifyProjectChangedEvent([moduleFile1, file1Consumer1], filesToLoad, [file1Consumer2]); + }); + + it("should be up-to-date with newly created files", () => { + const { moduleFile1, file1Consumer1, file1Consumer2, files, verifyProjectChangedEvent, } = getInitialState(); + + const file1Consumer3: FileOrFolder = { + path: "/a/b/file1Consumer3.ts", + content: `import {Foo} from "./moduleFile1"; let y = Foo();` + }; + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer1, file1Consumer3, file1Consumer2], files.concat(file1Consumer3)); + }); + + it("should detect changes in non-root files", () => { + const { moduleFile1, file1Consumer1, verifyProjectChangedEvent } = getInitialState({ + configObj: { files: [file1Consumer1Path] }, + firstCompilationEmitFiles: [file1Consumer1Path, moduleFile1Path, libFile.path] + }); + + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer1]); + + // change file1 internal, and verify only file1 is affected + moduleFile1.content += "var T1: number;"; + verifyProjectChangedEvent([moduleFile1]); + }); + + it("should return all files if a global file changed shape", () => { + const { globalFile3, verifyAffectedAllFiles } = getInitialState(); + + globalFile3.content += "var T2: string;"; + verifyAffectedAllFiles(); + }); + + it("should always return the file itself if '--isolatedModules' is specified", () => { + const { moduleFile1, verifyProjectChangedEvent } = getInitialState({ + configObj: { compilerOptions: { isolatedModules: true } } + }); + + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1]); + }); + + it("should always return the file itself if '--out' or '--outFile' is specified", () => { + const outFilePath = "/a/b/out.js"; + const { moduleFile1, verifyProjectChangedEvent } = getInitialState({ + configObj: { compilerOptions: { module: "system", outFile: outFilePath } } + }); + + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1]); + }); + + it("should return cascaded affected file list", () => { + const file1Consumer1Consumer1: FileOrFolder = { + path: "/a/b/file1Consumer1Consumer1.ts", + content: `import {y} from "./file1Consumer1";` + }; + const { moduleFile1, file1Consumer1, file1Consumer2, updateContentOfOpenFile, verifyProjectChangedEvent } = getInitialState({ + getAdditionalFileOrFolder: () => [file1Consumer1Consumer1] + }); + + updateContentOfOpenFile(file1Consumer1, file1Consumer1.content + "export var T: number;"); + verifyProjectChangedEvent([file1Consumer1, file1Consumer1Consumer1]); + + // Doesnt change the shape of file1Consumer1 + moduleFile1.content = `export var T: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer1, file1Consumer2]); + + // Change both files before the timeout + updateContentOfOpenFile(file1Consumer1, file1Consumer1.content + "export var T2: number;"); + moduleFile1.content = `export var T2: number;export function Foo() { };`; + verifyProjectChangedEvent([moduleFile1, file1Consumer1, file1Consumer2, file1Consumer1Consumer1]); + }); + + it("should work fine for files with circular references", () => { + const file1: FileOrFolder = { + path: "/a/b/file1.ts", + content: ` + /// + export var t1 = 10;` + }; + const file2: FileOrFolder = { + path: "/a/b/file2.ts", + content: ` + /// + export var t2 = 10;` + }; + const { configFile, verifyProjectChangedEvent, updateContentOfOpenFile } = getInitialState({ + getAdditionalFileOrFolder: () => [file1, file2], + firstReloadFileList: [file1.path, libFile.path, file2.path, configFilePath] + }); + + updateContentOfOpenFile(file1, file1.content + "export var t3 = 10;"); + verifyProjectChangedEvent([file1, file2], [file1, file2, libFile, configFile]); + }); + + it("should detect removed code file", () => { + const referenceFile1: FileOrFolder = { + path: "/a/b/referenceFile1.ts", + content: ` + /// + export var x = Foo();` + }; + const { configFile, verifyProjectChangedEvent, moduleFile1 } = getInitialState({ + getAdditionalFileOrFolder: () => [referenceFile1], + firstReloadFileList: [referenceFile1.path, libFile.path, moduleFile1Path, configFilePath] + }); + + verifyProjectChangedEvent([referenceFile1], [libFile, referenceFile1, configFile], [moduleFile1]); + }); + + it("should detect non-existing code file", () => { + const referenceFile1: FileOrFolder = { + path: "/a/b/referenceFile1.ts", + content: ` + /// + export var x = Foo();` + }; + const { configFile, moduleFile2, updateContentOfOpenFile, verifyProjectChangedEvent } = getInitialState({ + getAdditionalFileOrFolder: () => [referenceFile1], + firstReloadFileList: [referenceFile1.path, libFile.path, configFilePath] + }); + + updateContentOfOpenFile(referenceFile1, referenceFile1.content + "export var yy = Foo();"); + verifyProjectChangedEvent([referenceFile1], [libFile, referenceFile1, configFile]); + + // Create module File2 and see both files are saved + verifyProjectChangedEvent([referenceFile1, moduleFile2], [libFile, moduleFile2, referenceFile1, configFile]); + }); + }); + } + + describe("when event handler is set in the session", () => { + verifyProjectChangedEvent(createSessionWithProjectChangedEventHandler); + + function createSessionWithProjectChangedEventHandler(host: TestServerHost): ProjectChangeEventVerifier { + const projectChangedEvents: server.ProjectChangedEvent[] = []; + const session = createSession(host, { + eventHandler: e => { + if (e.eventName === server.ProjectChangedEvent) { + projectChangedEvents.push(e); + } + } + }); + + return { + session, + verifyProjectChangedEventHandler, + verifyInitialOpen: createVerifyInitialOpen(session, verifyProjectChangedEventHandler) + }; + + function eventToString(event: server.ProjectChangedEvent) { + const eventToModify = event && { + eventName: event.eventName, + data: { + project: event.data.project.getProjectName(), + changedFiles: event.data.changedFiles, + filesToEmit: event.data.filesToEmit + } + }; + return JSON.stringify(eventToModify); + } + + function eventsToString(events: ReadonlyArray) { + return "[" + map(events, eventToString).join(",") + "]"; + } + + function verifyProjectChangedEventHandler(expectedEvents: ReadonlyArray) { + assert.equal(projectChangedEvents.length, expectedEvents.length, `Incorrect number of events Actual: ${eventsToString(projectChangedEvents)} Expected: ${eventsToString(expectedEvents)}`); + forEach(projectChangedEvents, (actualEvent, i) => { + const expectedEvent = expectedEvents[i]; + assert.strictEqual(actualEvent.eventName, expectedEvent.eventName); + assert.strictEqual(actualEvent.data.project, expectedEvent.data.project); + verifyFiles("changedFiles", actualEvent.data.changedFiles, expectedEvent.data.changedFiles); + verifyFiles("filesToEmit", actualEvent.data.filesToEmit, expectedEvent.data.filesToEmit); + }); + + // Verified the events, reset them + projectChangedEvents.length = 0; + } + } + }); + + describe("when event handler is not set but session is created with canUseEvents = true", () => { + verifyProjectChangedEvent(createSessionThatUsesEvents); + + function createSessionThatUsesEvents(host: TestServerHost): ProjectChangeEventVerifier { + const session = createSession(host, { canUseEvents: true }); + + return { + session, + verifyProjectChangedEventHandler, + verifyInitialOpen: createVerifyInitialOpen(session, verifyProjectChangedEventHandler) + }; + + function verifyProjectChangedEventHandler(expected: ReadonlyArray) { + const expectedEvents: protocol.ProjectChangedEventBody[] = map(expected, e => { + return { + projectName: e.data.project.getProjectName(), + changedFiles: e.data.changedFiles, + fileNamesToEmit: e.data.filesToEmit + }; + }); + const outputEventRegex = /Content\-Length: [\d]+\r\n\r\n/; + const events: protocol.ProjectStructureChangedEvent[] = filter( + map( + host.getOutput(), s => convertToObject( + ts.parseJsonText("json.json", s.replace(outputEventRegex, "")), + [] + ) + ), + e => e.event === server.ProjectChangedEvent + ); + assert.equal(events.length, expectedEvents.length, `Incorrect number of events Actual: ${map(events, e => e.body)} Expected: ${expectedEvents}`); + forEach(events, (actualEvent, i) => { + const expectedEvent = expectedEvents[i]; + assert.strictEqual(actualEvent.body.projectName, expectedEvent.projectName); + verifyFiles("changedFiles", actualEvent.body.changedFiles, expectedEvent.changedFiles); + verifyFiles("fileNamesToEmit", actualEvent.body.fileNamesToEmit, expectedEvent.fileNamesToEmit); + }); + + // Verified the events, reset them + host.clearOutput(); + } + } + }); + }); } diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts new file mode 100644 index 0000000000..44beb83922 --- /dev/null +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -0,0 +1,550 @@ +/// + +namespace ts.TestFSWithWatch { + const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); + export const libFile: FileOrFolder = { + path: "/a/lib/lib.d.ts", + content: libFileContent + }; + + export const safeList = { + path: "/safeList.json", + content: JSON.stringify({ + commander: "commander", + express: "express", + jquery: "jquery", + lodash: "lodash", + moment: "moment", + chroma: "chroma-js" + }) + }; + + function getExecutingFilePathFromLibFile(): string { + return combinePaths(getDirectoryPath(libFile.path), "tsc.js"); + } + + interface TestServerHostCreationParameters { + useCaseSensitiveFileNames?: boolean; + executingFilePath?: string; + currentDirectory?: string; + newLine?: string; + } + + export function createWatchedSystem(fileOrFolderList: FileOrFolder[], params?: TestServerHostCreationParameters): TestServerHost { + if (!params) { + params = {}; + } + const host = new TestServerHost(/*withSafelist*/ false, + params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false, + params.executingFilePath || getExecutingFilePathFromLibFile(), + params.currentDirectory || "/", + fileOrFolderList, + params.newLine); + return host; + } + + export function createServerHost(fileOrFolderList: FileOrFolder[], params?: TestServerHostCreationParameters): TestServerHost { + if (!params) { + params = {}; + } + const host = new TestServerHost(/*withSafelist*/ true, + params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false, + params.executingFilePath || getExecutingFilePathFromLibFile(), + params.currentDirectory || "/", + fileOrFolderList, + params.newLine); + return host; + } + + export interface FileOrFolder { + path: string; + content?: string; + fileSize?: number; + } + + interface FSEntry { + path: Path; + fullPath: string; + } + + interface File extends FSEntry { + content: string; + fileSize?: number; + } + + interface Folder extends FSEntry { + entries: FSEntry[]; + } + + function isFolder(s: FSEntry): s is Folder { + return s && isArray((s).entries); + } + + function isFile(s: FSEntry): s is File { + return s && isString((s).content); + } + + function invokeWatcherCallbacks(callbacks: T[], invokeCallback: (cb: T) => void): void { + if (callbacks) { + // The array copy is made to ensure that even if one of the callback removes the callbacks, + // we dont miss any callbacks following it + const cbs = callbacks.slice(); + for (const cb of cbs) { + invokeCallback(cb); + } + } + } + + function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { + assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}`); + for (const name of expectedKeys) { + assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`); + } + } + + export function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { + assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`); + for (const f of expectedFileNames) { + assert.isTrue(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${JSON.stringify(actualFileNames)}`); + } + } + + export function checkWatchedFiles(host: TestServerHost, expectedFiles: string[]) { + checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles); + } + + export function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[], recursive = false) { + checkMapKeys("watchedDirectories", recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories); + } + + export function checkOutputContains(host: TestServerHost, expected: ReadonlyArray) { + const mapExpected = arrayToSet(expected); + const mapSeen = createMap(); + for (const f of host.getOutput()) { + assert.isUndefined(mapSeen.get(f), `Already found ${f} in ${JSON.stringify(host.getOutput())}`); + if (mapExpected.has(f)) { + mapExpected.delete(f); + mapSeen.set(f, true); + } + } + assert.equal(mapExpected.size, 0, `Output has missing ${JSON.stringify(flatMapIter(mapExpected.keys(), key => key))} in ${JSON.stringify(host.getOutput())}`); + } + + export function checkOutputDoesNotContain(host: TestServerHost, expectedToBeAbsent: string[] | ReadonlyArray) { + const mapExpectedToBeAbsent = arrayToSet(expectedToBeAbsent); + for (const f of host.getOutput()) { + assert.isFalse(mapExpectedToBeAbsent.has(f), `Contains ${f} in ${JSON.stringify(host.getOutput())}`); + } + } + + class Callbacks { + private map: TimeOutCallback[] = []; + private nextId = 1; + + register(cb: (...args: any[]) => void, args: any[]) { + const timeoutId = this.nextId; + this.nextId++; + this.map[timeoutId] = cb.bind(/*this*/ undefined, ...args); + return timeoutId; + } + + unregister(id: any) { + if (typeof id === "number") { + delete this.map[id]; + } + } + + count() { + let n = 0; + for (const _ in this.map) { + n++; + } + return n; + } + + invoke() { + // Note: invoking a callback may result in new callbacks been queued, + // so do not clear the entire callback list regardless. Only remove the + // ones we have invoked. + for (const key in this.map) { + this.map[key](); + delete this.map[key]; + } + } + } + + type TimeOutCallback = () => any; + + export class TestServerHost implements server.ServerHost { + args: string[] = []; + + private readonly output: string[] = []; + + private fs: Map = createMap(); + private getCanonicalFileName: (s: string) => string; + private toPath: (f: string) => Path; + private timeoutCallbacks = new Callbacks(); + private immediateCallbacks = new Callbacks(); + + readonly watchedDirectories = createMultiMap(); + readonly watchedDirectoriesRecursive = createMultiMap(); + readonly watchedFiles = createMultiMap(); + + constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[], public readonly newLine = "\n") { + this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName); + + this.reloadFS(fileOrFolderList); + } + + toNormalizedAbsolutePath(s: string) { + return getNormalizedAbsolutePath(s, this.currentDirectory); + } + + toFullPath(s: string) { + return this.toPath(this.toNormalizedAbsolutePath(s)); + } + + reloadFS(fileOrFolderList: FileOrFolder[]) { + const mapNewLeaves = createMap(); + const isNewFs = this.fs.size === 0; + // always inject safelist file in the list of files + for (const fileOrFolder of fileOrFolderList.concat(this.withSafeList ? safeList : [])) { + const path = this.toFullPath(fileOrFolder.path); + mapNewLeaves.set(path, true); + // If its a change + const currentEntry = this.fs.get(path); + if (currentEntry) { + if (isFile(currentEntry)) { + if (isString(fileOrFolder.content)) { + // Update file + if (currentEntry.content !== fileOrFolder.content) { + currentEntry.content = fileOrFolder.content; + this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed); + } + } + else { + // TODO: Changing from file => folder + } + } + else { + // Folder + if (isString(fileOrFolder.content)) { + // TODO: Changing from folder => file + } + else { + // Folder update: Nothing to do. + } + } + } + else { + this.ensureFileOrFolder(fileOrFolder); + } + } + + if (!isNewFs) { + this.fs.forEach((fileOrFolder, path) => { + // If this entry is not from the new file or folder + if (!mapNewLeaves.get(path)) { + // Leaf entries that arent in new list => remove these + if (isFile(fileOrFolder) || isFolder(fileOrFolder) && fileOrFolder.entries.length === 0) { + this.removeFileOrFolder(fileOrFolder, folder => !mapNewLeaves.get(folder.path)); + } + } + }); + } + } + + ensureFileOrFolder(fileOrFolder: FileOrFolder) { + if (isString(fileOrFolder.content)) { + const file = this.toFile(fileOrFolder); + Debug.assert(!this.fs.get(file.path)); + const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath)); + this.addFileOrFolderInFolder(baseFolder, file); + } + else { + const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory); + this.ensureFolder(fullPath); + } + } + + private ensureFolder(fullPath: string): Folder { + const path = this.toPath(fullPath); + let folder = this.fs.get(path) as Folder; + if (!folder) { + folder = this.toFolder(fullPath); + const baseFullPath = getDirectoryPath(fullPath); + if (fullPath !== baseFullPath) { + // Add folder in the base folder + const baseFolder = this.ensureFolder(baseFullPath); + this.addFileOrFolderInFolder(baseFolder, folder); + } + else { + // root folder + Debug.assert(this.fs.size === 0); + this.fs.set(path, folder); + } + } + Debug.assert(isFolder(folder)); + return folder; + } + + private addFileOrFolderInFolder(folder: Folder, fileOrFolder: File | Folder) { + folder.entries.push(fileOrFolder); + this.fs.set(fileOrFolder.path, fileOrFolder); + + if (isFile(fileOrFolder)) { + this.invokeFileWatcher(fileOrFolder.fullPath, FileWatcherEventKind.Created); + } + this.invokeDirectoryWatcher(folder.fullPath, fileOrFolder.fullPath); + } + + private removeFileOrFolder(fileOrFolder: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean) { + const basePath = getDirectoryPath(fileOrFolder.path); + const baseFolder = this.fs.get(basePath) as Folder; + if (basePath !== fileOrFolder.path) { + Debug.assert(!!baseFolder); + filterMutate(baseFolder.entries, entry => entry !== fileOrFolder); + } + this.fs.delete(fileOrFolder.path); + + if (isFile(fileOrFolder)) { + this.invokeFileWatcher(fileOrFolder.fullPath, FileWatcherEventKind.Deleted); + } + else { + Debug.assert(fileOrFolder.entries.length === 0); + const relativePath = this.getRelativePathToDirectory(fileOrFolder.fullPath, fileOrFolder.fullPath); + // Invoke directory and recursive directory watcher for the folder + // Here we arent invoking recursive directory watchers for the base folders + // since that is something we would want to do for both file as well as folder we are deleting + invokeWatcherCallbacks(this.watchedDirectories.get(fileOrFolder.path), cb => cb(relativePath)); + invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrFolder.path), cb => cb(relativePath)); + } + + if (basePath !== fileOrFolder.path) { + if (baseFolder.entries.length === 0 && isRemovableLeafFolder(baseFolder)) { + this.removeFileOrFolder(baseFolder, isRemovableLeafFolder); + } + else { + this.invokeRecursiveDirectoryWatcher(baseFolder.fullPath, fileOrFolder.fullPath); + } + } + } + + private invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind) { + const callbacks = this.watchedFiles.get(this.toPath(fileFullPath)); + const fileName = getBaseFileName(fileFullPath); + invokeWatcherCallbacks(callbacks, cb => cb(fileName, eventKind)); + } + + private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) { + return getRelativePathToDirectoryOrUrl(directoryFullPath, fileFullPath, this.currentDirectory, this.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false); + } + + /** + * This will call the directory watcher for the folderFullPath and recursive directory watchers for this and base folders + */ + private invokeDirectoryWatcher(folderFullPath: string, fileName: string) { + const relativePath = this.getRelativePathToDirectory(folderFullPath, fileName); + invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => cb(relativePath)); + this.invokeRecursiveDirectoryWatcher(folderFullPath, fileName); + } + + /** + * This will call the recursive directory watcher for this directory as well as all the base directories + */ + private invokeRecursiveDirectoryWatcher(fullPath: string, fileName: string) { + const relativePath = this.getRelativePathToDirectory(fullPath, fileName); + invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), cb => cb(relativePath)); + const basePath = getDirectoryPath(fullPath); + if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) { + this.invokeRecursiveDirectoryWatcher(basePath, fileName); + } + } + + private toFile(fileOrFolder: FileOrFolder): File { + const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory); + return { + path: this.toPath(fullPath), + content: fileOrFolder.content, + fullPath, + fileSize: fileOrFolder.fileSize + }; + } + + private toFolder(path: string): Folder { + const fullPath = getNormalizedAbsolutePath(path, this.currentDirectory); + return { + path: this.toPath(fullPath), + entries: [], + fullPath + }; + } + + fileExists(s: string) { + const path = this.toFullPath(s); + return isFile(this.fs.get(path)); + } + + readFile(s: string) { + const fsEntry = this.fs.get(this.toFullPath(s)); + return isFile(fsEntry) ? fsEntry.content : undefined; + } + + getFileSize(s: string) { + const path = this.toFullPath(s); + const entry = this.fs.get(path); + if (isFile(entry)) { + return entry.fileSize ? entry.fileSize : entry.content.length; + } + return undefined; + } + + directoryExists(s: string) { + const path = this.toFullPath(s); + return isFolder(this.fs.get(path)); + } + + getDirectories(s: string) { + const path = this.toFullPath(s); + const folder = this.fs.get(path); + if (isFolder(folder)) { + return mapDefined(folder.entries, entry => isFolder(entry) ? getBaseFileName(entry.fullPath) : undefined); + } + Debug.fail(folder ? "getDirectories called on file" : "getDirectories called on missing folder"); + return []; + } + + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { + return ts.matchFiles(this.toNormalizedAbsolutePath(path), extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => { + const directories: string[] = []; + const files: string[] = []; + const dirEntry = this.fs.get(this.toPath(dir)); + if (isFolder(dirEntry)) { + dirEntry.entries.forEach((entry) => { + if (isFolder(entry)) { + directories.push(getBaseFileName(entry.fullPath)); + } + else if (isFile(entry)) { + files.push(getBaseFileName(entry.fullPath)); + } + else { + Debug.fail("Unknown entry"); + } + }); + } + return { directories, files }; + }); + } + + watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { + const path = this.toFullPath(directoryName); + const map = recursive ? this.watchedDirectoriesRecursive : this.watchedDirectories; + map.add(path, callback); + return { + referenceCount: 0, + directoryName, + close: () => map.remove(path, callback) + }; + } + + createHash(s: string): string { + return Harness.mockHash(s); + } + + watchFile(fileName: string, callback: FileWatcherCallback) { + const path = this.toFullPath(fileName); + this.watchedFiles.add(path, callback); + return { close: () => this.watchedFiles.remove(path, callback) }; + } + + // TOOD: record and invoke callbacks to simulate timer events + setTimeout(callback: TimeOutCallback, _time: number, ...args: any[]) { + return this.timeoutCallbacks.register(callback, args); + } + + clearTimeout(timeoutId: any): void { + this.timeoutCallbacks.unregister(timeoutId); + } + + checkTimeoutQueueLengthAndRun(expected: number) { + this.checkTimeoutQueueLength(expected); + this.runQueuedTimeoutCallbacks(); + } + + checkTimeoutQueueLength(expected: number) { + const callbacksCount = this.timeoutCallbacks.count(); + assert.equal(callbacksCount, expected, `expected ${expected} timeout callbacks queued but found ${callbacksCount}.`); + } + + runQueuedTimeoutCallbacks() { + try { + this.timeoutCallbacks.invoke(); + } + catch (e) { + if (e.message === this.existMessage) { + return; + } + throw e; + } + } + + runQueuedImmediateCallbacks() { + this.immediateCallbacks.invoke(); + } + + setImmediate(callback: TimeOutCallback, _time: number, ...args: any[]) { + return this.immediateCallbacks.register(callback, args); + } + + clearImmediate(timeoutId: any): void { + this.immediateCallbacks.unregister(timeoutId); + } + + createDirectory(directoryName: string): void { + const folder = this.toFolder(directoryName); + + // base folder has to be present + const base = getDirectoryPath(folder.fullPath); + const baseFolder = this.fs.get(base) as Folder; + Debug.assert(isFolder(baseFolder)); + + Debug.assert(!this.fs.get(folder.path)); + this.addFileOrFolderInFolder(baseFolder, folder); + } + + writeFile(path: string, content: string): void { + const file = this.toFile({ path, content }); + + // base folder has to be present + const base = getDirectoryPath(file.fullPath); + const folder = this.fs.get(base) as Folder; + Debug.assert(isFolder(folder)); + + this.addFileOrFolderInFolder(folder, file); + } + + write(message: string) { + this.output.push(message); + } + + getOutput(): ReadonlyArray { + return this.output; + } + + clearOutput() { + clear(this.output); + } + + readonly existMessage = "System Exit"; + exitCode: number; + readonly resolvePath = (s: string) => s; + readonly getExecutingFilePath = () => this.executingFilePath; + readonly getCurrentDirectory = () => this.currentDirectory; + exit(exitCode?: number) { + this.exitCode = exitCode; + throw new Error(this.existMessage); + } + readonly getEnvironmentVariable = notImplemented; + } +} diff --git a/src/server/builder.ts b/src/server/builder.ts deleted file mode 100644 index 8a10682b4c..0000000000 --- a/src/server/builder.ts +++ /dev/null @@ -1,359 +0,0 @@ -/// -/// -/// - -namespace ts.server { - - export function shouldEmitFile(scriptInfo: ScriptInfo) { - return !scriptInfo.hasMixedContent; - } - - /** - * An abstract file info that maintains a shape signature. - */ - export class BuilderFileInfo { - - private lastCheckedShapeSignature: string; - - constructor(public readonly scriptInfo: ScriptInfo, public readonly project: Project) { - } - - public isExternalModuleOrHasOnlyAmbientExternalModules() { - const sourceFile = this.getSourceFile(); - return isExternalModule(sourceFile) || this.containsOnlyAmbientModules(sourceFile); - } - - /** - * For script files that contains only ambient external modules, although they are not actually external module files, - * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, - * there are no point to rebuild all script files if these special files have changed. However, if any statement - * in the file is not ambient external module, we treat it as a regular script file. - */ - private containsOnlyAmbientModules(sourceFile: SourceFile) { - for (const statement of sourceFile.statements) { - if (statement.kind !== SyntaxKind.ModuleDeclaration || (statement).name.kind !== SyntaxKind.StringLiteral) { - return false; - } - } - return true; - } - - private computeHash(text: string): string { - return this.project.projectService.host.createHash(text); - } - - private getSourceFile(): SourceFile { - return this.project.getSourceFile(this.scriptInfo.path); - } - - /** - * @return {boolean} indicates if the shape signature has changed since last update. - */ - public updateShapeSignature() { - const sourceFile = this.getSourceFile(); - if (!sourceFile) { - return true; - } - - const lastSignature = this.lastCheckedShapeSignature; - if (sourceFile.isDeclarationFile) { - this.lastCheckedShapeSignature = this.computeHash(sourceFile.text); - } - else { - const emitOutput = this.project.getFileEmitOutput(this.scriptInfo, /*emitOnlyDtsFiles*/ true); - if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { - this.lastCheckedShapeSignature = this.computeHash(emitOutput.outputFiles[0].text); - } - } - return !lastSignature || this.lastCheckedShapeSignature !== lastSignature; - } - } - - export interface Builder { - readonly project: Project; - getFilesAffectedBy(scriptInfo: ScriptInfo): string[]; - onProjectUpdateGraph(): void; - emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean; - clear(): void; - } - - abstract class AbstractBuilder implements Builder { - - /** - * stores set of files from the project. - * NOTE: this field is created on demand and should not be accessed directly. - * Use 'getFileInfos' instead. - */ - private fileInfos_doNotAccessDirectly: Map; - - constructor(public readonly project: Project, private ctor: { new (scriptInfo: ScriptInfo, project: Project): T }) { - } - - private getFileInfos() { - return this.fileInfos_doNotAccessDirectly || (this.fileInfos_doNotAccessDirectly = createMap()); - } - - protected hasFileInfos() { - return !!this.fileInfos_doNotAccessDirectly; - } - - public clear() { - // drop the existing list - it will be re-created as necessary - this.fileInfos_doNotAccessDirectly = undefined; - } - - protected getFileInfo(path: Path): T { - return this.getFileInfos().get(path); - } - - protected getOrCreateFileInfo(path: Path): T { - let fileInfo = this.getFileInfo(path); - if (!fileInfo) { - const scriptInfo = this.project.getScriptInfo(path); - fileInfo = new this.ctor(scriptInfo, this.project); - this.setFileInfo(path, fileInfo); - } - return fileInfo; - } - - protected getFileInfoPaths(): Path[] { - return arrayFrom(this.getFileInfos().keys() as Iterator); - } - - protected setFileInfo(path: Path, info: T) { - this.getFileInfos().set(path, info); - } - - protected removeFileInfo(path: Path) { - this.getFileInfos().delete(path); - } - - protected forEachFileInfo(action: (fileInfo: T) => any) { - this.getFileInfos().forEach(action); - } - - abstract getFilesAffectedBy(scriptInfo: ScriptInfo): string[]; - abstract onProjectUpdateGraph(): void; - protected abstract ensureFileInfoIfInProject(scriptInfo: ScriptInfo): void; - - /** - * @returns {boolean} whether the emit was conducted or not - */ - emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean { - this.ensureFileInfoIfInProject(scriptInfo); - const fileInfo = this.getFileInfo(scriptInfo.path); - if (!fileInfo) { - return false; - } - - const { emitSkipped, outputFiles } = this.project.getFileEmitOutput(fileInfo.scriptInfo, /*emitOnlyDtsFiles*/ false); - if (!emitSkipped) { - const projectRootPath = this.project.getProjectRootPath(); - for (const outputFile of outputFiles) { - const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, projectRootPath ? projectRootPath : getDirectoryPath(scriptInfo.fileName)); - writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark); - } - } - return !emitSkipped; - } - } - - class NonModuleBuilder extends AbstractBuilder { - - constructor(public readonly project: Project) { - super(project, BuilderFileInfo); - } - - protected ensureFileInfoIfInProject(scriptInfo: ScriptInfo) { - if (this.project.containsScriptInfo(scriptInfo)) { - this.getOrCreateFileInfo(scriptInfo.path); - } - } - - onProjectUpdateGraph() { - if (this.hasFileInfos()) { - this.forEachFileInfo(fileInfo => { - if (!this.project.containsScriptInfo(fileInfo.scriptInfo)) { - // This file was deleted from this project - this.removeFileInfo(fileInfo.scriptInfo.path); - } - }); - } - } - - /** - * Note: didn't use path as parameter because the returned file names will be directly - * consumed by the API user, which will use it to interact with file systems. Path - * should only be used internally, because the case sensitivity is not trustable. - */ - getFilesAffectedBy(scriptInfo: ScriptInfo): string[] { - const info = this.getOrCreateFileInfo(scriptInfo.path); - const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName]; - if (info.updateShapeSignature()) { - const options = this.project.getCompilerOptions(); - // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, - // so returning the file itself is good enough. - if (options && (options.out || options.outFile)) { - return singleFileResult; - } - return this.project.getAllEmittableFiles(); - } - return singleFileResult; - } - } - - class ModuleBuilderFileInfo extends BuilderFileInfo { - references = createSortedArray(); - readonly referencedBy = createSortedArray(); - scriptVersionForReferences: string; - - static compareFileInfos(lf: ModuleBuilderFileInfo, rf: ModuleBuilderFileInfo): Comparison { - return compareStrings(lf.scriptInfo.fileName, rf.scriptInfo.fileName); - } - - addReferencedBy(fileInfo: ModuleBuilderFileInfo): void { - insertSorted(this.referencedBy, fileInfo, ModuleBuilderFileInfo.compareFileInfos); - } - - removeReferencedBy(fileInfo: ModuleBuilderFileInfo): void { - removeSorted(this.referencedBy, fileInfo, ModuleBuilderFileInfo.compareFileInfos); - } - - removeFileReferences() { - for (const reference of this.references) { - reference.removeReferencedBy(this); - } - clear(this.references); - } - } - - class ModuleBuilder extends AbstractBuilder { - - constructor(public readonly project: Project) { - super(project, ModuleBuilderFileInfo); - } - - private projectVersionForDependencyGraph: string; - - public clear() { - this.projectVersionForDependencyGraph = undefined; - super.clear(); - } - - private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): SortedArray { - if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) { - return createSortedArray(); - } - - const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path); - return toSortedArray(referencedFilePaths.map(f => this.getOrCreateFileInfo(f)), ModuleBuilderFileInfo.compareFileInfos); - } - - protected ensureFileInfoIfInProject(_scriptInfo: ScriptInfo) { - this.ensureProjectDependencyGraphUpToDate(); - } - - onProjectUpdateGraph() { - // Update the graph only if we have computed graph earlier - if (this.hasFileInfos()) { - this.ensureProjectDependencyGraphUpToDate(); - } - } - - private ensureProjectDependencyGraphUpToDate() { - if (!this.projectVersionForDependencyGraph || this.project.getProjectVersion() !== this.projectVersionForDependencyGraph) { - const currentScriptInfos = this.project.getScriptInfos(); - for (const scriptInfo of currentScriptInfos) { - const fileInfo = this.getOrCreateFileInfo(scriptInfo.path); - this.updateFileReferences(fileInfo); - } - this.forEachFileInfo(fileInfo => { - if (!this.project.containsScriptInfo(fileInfo.scriptInfo)) { - // This file was deleted from this project - fileInfo.removeFileReferences(); - this.removeFileInfo(fileInfo.scriptInfo.path); - } - }); - this.projectVersionForDependencyGraph = this.project.getProjectVersion(); - } - } - - private updateFileReferences(fileInfo: ModuleBuilderFileInfo) { - // Only need to update if the content of the file changed. - if (fileInfo.scriptVersionForReferences === fileInfo.scriptInfo.getLatestVersion()) { - return; - } - - const newReferences = this.getReferencedFileInfos(fileInfo); - const oldReferences = fileInfo.references; - enumerateInsertsAndDeletes(newReferences, oldReferences, - /*inserted*/ newReference => newReference.addReferencedBy(fileInfo), - /*deleted*/ oldReference => { - // New reference is greater then current reference. That means - // the current reference doesn't exist anymore after parsing. So delete - // references. - oldReference.removeReferencedBy(fileInfo); - }, - /*compare*/ ModuleBuilderFileInfo.compareFileInfos); - - fileInfo.references = newReferences; - fileInfo.scriptVersionForReferences = fileInfo.scriptInfo.getLatestVersion(); - } - - getFilesAffectedBy(scriptInfo: ScriptInfo): string[] { - this.ensureProjectDependencyGraphUpToDate(); - - const singleFileResult = scriptInfo.hasMixedContent ? [] : [scriptInfo.fileName]; - const fileInfo = this.getFileInfo(scriptInfo.path); - if (!fileInfo || !fileInfo.updateShapeSignature()) { - return singleFileResult; - } - - if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) { - return this.project.getAllEmittableFiles(); - } - - const options = this.project.getCompilerOptions(); - if (options && (options.isolatedModules || options.out || options.outFile)) { - return singleFileResult; - } - - // Now we need to if each file in the referencedBy list has a shape change as well. - // Because if so, its own referencedBy files need to be saved as well to make the - // emitting result consistent with files on disk. - - // Use slice to clone the array to avoid manipulating in place - const queue = fileInfo.referencedBy.slice(0); - const fileNameSet = createMap(); - fileNameSet.set(scriptInfo.fileName, scriptInfo); - while (queue.length > 0) { - const processingFileInfo = queue.pop(); - if (processingFileInfo.updateShapeSignature() && processingFileInfo.referencedBy.length > 0) { - for (const potentialFileInfo of processingFileInfo.referencedBy) { - if (!fileNameSet.has(potentialFileInfo.scriptInfo.fileName)) { - queue.push(potentialFileInfo); - } - } - } - fileNameSet.set(processingFileInfo.scriptInfo.fileName, processingFileInfo.scriptInfo); - } - const result: string[] = []; - fileNameSet.forEach((scriptInfo, fileName) => { - if (shouldEmitFile(scriptInfo)) { - result.push(fileName); - } - }); - return result; - } - } - - export function createBuilder(project: Project): Builder { - const moduleKind = project.getCompilerOptions().module; - switch (moduleKind) { - case ModuleKind.None: - return new NonModuleBuilder(project); - default: - return new ModuleBuilder(project); - } - } -} diff --git a/src/server/client.ts b/src/server/client.ts index 4acd862a89..f2ab678e6f 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -342,7 +342,7 @@ namespace ts.server { convertDiagnostic(entry: protocol.DiagnosticWithLinePosition, _fileName: string): Diagnostic { let category: DiagnosticCategory; for (const id in DiagnosticCategory) { - if (typeof id === "string" && entry.category === id.toLowerCase()) { + if (isString(id) && entry.category === id.toLowerCase()) { category = (DiagnosticCategory)[id]; } } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 817f9eee60..385ade0b81 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -10,14 +10,14 @@ namespace ts.server { export const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024; - export const ContextEvent = "context"; + export const ProjectChangedEvent = "projectChanged"; export const ConfigFileDiagEvent = "configFileDiag"; export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState"; export const ProjectInfoTelemetryEvent = "projectInfo"; - export interface ContextEvent { - eventName: typeof ContextEvent; - data: { project: Project; fileName: NormalizedPath }; + export interface ProjectChangedEvent { + eventName: typeof ProjectChangedEvent; + data: { project: Project; filesToEmit: string[]; changedFiles: string[] }; } export interface ConfigFileDiagEvent { @@ -77,7 +77,7 @@ namespace ts.server { readonly dts: number; } - export type ProjectServiceEvent = ContextEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent; + export type ProjectServiceEvent = ProjectChangedEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent; export interface ProjectServiceEventHandler { (event: ProjectServiceEvent): void; @@ -159,7 +159,7 @@ namespace ts.server { }; export function convertFormatOptions(protocolOptions: protocol.FormatCodeSettings): FormatCodeSettings { - if (typeof protocolOptions.indentStyle === "string") { + if (isString(protocolOptions.indentStyle)) { protocolOptions.indentStyle = indentStyle.get(protocolOptions.indentStyle.toLowerCase()); Debug.assert(protocolOptions.indentStyle !== undefined); } @@ -169,7 +169,7 @@ namespace ts.server { export function convertCompilerOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): CompilerOptions & protocol.CompileOnSaveMixin { compilerOptionConverters.forEach((mappedValues, id) => { const propertyValue = protocolOptions[id]; - if (typeof propertyValue === "string") { + if (isString(propertyValue)) { protocolOptions[id] = mappedValues.get(propertyValue.toLowerCase()); } }); @@ -177,9 +177,7 @@ namespace ts.server { } export function tryConvertScriptKindName(scriptKindName: protocol.ScriptKindName | ScriptKind): ScriptKind { - return typeof scriptKindName === "string" - ? convertScriptKindName(scriptKindName) - : scriptKindName; + return isString(scriptKindName) ? convertScriptKindName(scriptKindName) : scriptKindName; } export function convertScriptKindName(scriptKindName: protocol.ScriptKindName) { @@ -249,7 +247,8 @@ namespace ts.server { WildcardDirectories = "Wild card directory", TypeRoot = "Type root of the project", ClosedScriptInfo = "Closed Script info", - ConfigFileForInferredRoot = "Config file for the inferred project root" + ConfigFileForInferredRoot = "Config file for the inferred project root", + FailedLookupLocation = "Failed lookup locations in module resolution" } /* @internal */ @@ -493,9 +492,32 @@ namespace ts.server { if (this.pendingProjectUpdates.delete(projectName)) { project.updateGraph(); } + // Send the update event to notify about the project changes + this.sendProjectChangedEvent(project); }); } + private sendProjectChangedEvent(project: Project) { + if (project.isClosed() || !this.eventHandler || !project.languageServiceEnabled) { + return; + } + + const { filesToEmit, changedFiles } = project.getChangedFiles(); + if (changedFiles.length === 0) { + return; + } + + const event: ProjectChangedEvent = { + eventName: ProjectChangedEvent, + data: { + project, + filesToEmit: filesToEmit as string[], + changedFiles: changedFiles as string[] + } + }; + this.eventHandler(event); + } + /* @internal */ delayUpdateProjectGraphAndInferredProjectsRefresh(project: Project) { this.delayUpdateProjectGraph(project); @@ -567,6 +589,11 @@ namespace ts.server { return scriptInfo && !scriptInfo.isOrphan() && scriptInfo.getDefaultProject(); } + getScriptInfoEnsuringProjectsUptoDate(uncheckedFileName: string) { + this.ensureProjectStructuresUptoDate(); + return this.getScriptInfo(uncheckedFileName); + } + /** * Ensures the project structures are upto date * This means, @@ -674,25 +701,12 @@ namespace ts.server { // update projects to make sure that set of referenced files is correct this.delayUpdateProjectGraphs(containingProjects); - - // TODO: (sheetalkamat) Someway to send this event so that error checks are updated? - // if (!this.eventHandler) { - // return; - // } - - // for (const openFile of this.openFiles) { - // const event: ContextEvent = { - // eventName: ContextEvent, - // data: { project: openFile.getDefaultProject(), fileName: openFile.fileName } - // }; - // this.eventHandler(event); - // } } } /* @internal */ - onTypeRootFileChanged(project: ConfiguredProject, fileName: NormalizedPath) { - project.getCachedServerHost().addOrDeleteFileOrFolder(fileName); + onTypeRootFileChanged(project: ConfiguredProject, fileOrFolder: NormalizedPath) { + project.getCachedServerHost().addOrDeleteFileOrFolder(fileOrFolder, this.toPath(fileOrFolder)); project.updateTypes(); this.delayUpdateProjectGraphAndInferredProjectsRefresh(project); } @@ -703,15 +717,15 @@ namespace ts.server { * @param fileName the absolute file name that changed in watched directory */ /* @internal */ - onFileAddOrRemoveInWatchedDirectoryOfProject(project: ConfiguredProject, fileName: NormalizedPath) { - project.getCachedServerHost().addOrDeleteFileOrFolder(fileName); + onFileAddOrRemoveInWatchedDirectoryOfProject(project: ConfiguredProject, fileOrFolder: NormalizedPath) { + project.getCachedServerHost().addOrDeleteFileOrFolder(fileOrFolder, this.toPath(fileOrFolder)); const configFilename = project.getConfigFilePath(); // If a change was made inside "folder/file", node will trigger the callback twice: // one with the fileName being "folder/file", and the other one with "folder". // We don't respond to the second one. - if (fileName && !isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) { - this.logger.info(`Project: ${configFilename} Detected file add/remove of non supported extension: ${fileName}`); + if (fileOrFolder && !isSupportedSourceFileName(fileOrFolder, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) { + this.logger.info(`Project: ${configFilename} Detected file add/remove of non supported extension: ${fileOrFolder}`); return; } @@ -1019,7 +1033,7 @@ namespace ts.server { if (this.configuredProjects.has(canonicalConfigFilePath)) { watches.push(WatchType.ConfigFilePath); } - this.logger.info(`ConfigFilePresence:: Current Watches: ['${watches.join("','")}']:: File: ${configFileName} Currently impacted open files: RootsOfInferredProjects: ${inferredRoots} OtherOpenFiles: ${otherFiles} Status: ${status}`); + this.logger.info(`ConfigFilePresence:: Current Watches: ${watches}:: File: ${configFileName} Currently impacted open files: RootsOfInferredProjects: ${inferredRoots} OtherOpenFiles: ${otherFiles} Status: ${status}`); } /** @@ -1371,7 +1385,7 @@ namespace ts.server { } private createConfiguredProject(configFileName: NormalizedPath, clientFileName?: string) { - const cachedServerHost = new CachedServerHost(this.host, this.toCanonicalFileName); + const cachedServerHost = new CachedServerHost(this.host); const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedServerHost); this.logger.info(`Opened configuration file ${configFileName}`); const languageServiceEnabled = !this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader); @@ -1425,7 +1439,7 @@ namespace ts.server { else { const scriptKind = propertyReader.getScriptKind(f); const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions); - scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent); + scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent, project.lsHost.host); path = scriptInfo.path; // If this script info is not already a root add it if (!project.isRoot(scriptInfo)) { @@ -1579,9 +1593,12 @@ namespace ts.server { * @param uncheckedFileName is absolute pathname * @param fileContent is a known version of the file content that is more up to date than the one on disk */ - - getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind) { - return this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName), openedByClient, fileContent, scriptKind); + /*@internal*/ + getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, hostToQueryFileExistsOn: ServerHost) { + return this.getOrCreateScriptInfoForNormalizedPath( + toNormalizedPath(uncheckedFileName), openedByClient, /*fileContent*/ undefined, + /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, hostToQueryFileExistsOn + ); } getScriptInfo(uncheckedFileName: string) { @@ -1606,11 +1623,11 @@ namespace ts.server { } } - getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) { + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: ServerHost) { const path = normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName); let info = this.getScriptInfoForPath(path); if (!info) { - if (openedByClient || this.host.fileExists(fileName)) { + if (openedByClient || (hostToQueryFileExistsOn || this.host).fileExists(fileName)) { info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path); this.filenameToScriptInfo.set(info.path, info); @@ -2063,7 +2080,7 @@ namespace ts.server { // RegExp group numbers are 1-based, but the first element in groups // is actually the original string, so it all works out in the end. if (typeof groupNumberOrString === "number") { - if (typeof groups[groupNumberOrString] !== "string") { + if (!isString(groups[groupNumberOrString])) { // Specification was wrong - exclude nothing! this.logger.info(`Incorrect RegExp specification in safelist rule ${name} - not enough groups`); // * can't appear in a filename; escape it because it's feeding into a RegExp diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index 3bba551322..44b9893bc3 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -1,20 +1,21 @@ /// /// /// +/// namespace ts.server { + /*@internal*/ export class CachedServerHost implements ServerHost { args: string[]; newLine: string; useCaseSensitiveFileNames: boolean; + private readonly cachedPartialSystem: CachedPartialSystem; + readonly trace: (s: string) => void; readonly realpath?: (path: string) => string; - private cachedReadDirectoryResult = createMap(); - private readonly currentDirectory: string; - - constructor(private readonly host: ServerHost, private getCanonicalFileName: (fileName: string) => string) { + constructor(private readonly host: ServerHost) { this.args = host.args; this.newLine = host.newLine; this.useCaseSensitiveFileNames = host.useCaseSensitiveFileNames; @@ -24,41 +25,7 @@ namespace ts.server { if (this.host.realpath) { this.realpath = path => this.host.realpath(path); } - this.currentDirectory = this.host.getCurrentDirectory(); - } - - private toPath(fileName: string) { - return toPath(fileName, this.currentDirectory, this.getCanonicalFileName); - } - - private getFileSystemEntries(rootDir: string) { - const path = this.toPath(rootDir); - const cachedResult = this.cachedReadDirectoryResult.get(path); - if (cachedResult) { - return cachedResult; - } - - const resultFromHost: FileSystemEntries = { - files: this.host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]) || [], - directories: this.host.getDirectories(rootDir) || [] - }; - - this.cachedReadDirectoryResult.set(path, resultFromHost); - return resultFromHost; - } - - private canWorkWithCacheForDir(rootDir: string) { - // Some of the hosts might not be able to handle read directory or getDirectories - const path = this.toPath(rootDir); - if (this.cachedReadDirectoryResult.get(path)) { - return true; - } - try { - return this.getFileSystemEntries(rootDir); - } - catch (_e) { - return false; - } + this.cachedPartialSystem = createCachedPartialSystem(host); } write(s: string) { @@ -66,13 +33,7 @@ namespace ts.server { } writeFile(fileName: string, data: string, writeByteOrderMark?: boolean) { - const path = this.toPath(fileName); - const result = this.cachedReadDirectoryResult.get(getDirectoryPath(path)); - const baseFileName = getBaseFileName(toNormalizedPath(fileName)); - if (result) { - result.files = this.updateFileSystemEntry(result.files, baseFileName, /*isValid*/ true); - } - return this.host.writeFile(fileName, data, writeByteOrderMark); + this.cachedPartialSystem.writeFile(fileName, data, writeByteOrderMark); } resolvePath(path: string) { @@ -88,7 +49,7 @@ namespace ts.server { } getCurrentDirectory() { - return this.currentDirectory; + return this.cachedPartialSystem.getCurrentDirectory(); } exit(exitCode?: number) { @@ -101,78 +62,35 @@ namespace ts.server { } getDirectories(rootDir: string) { - if (this.canWorkWithCacheForDir(rootDir)) { - return this.getFileSystemEntries(rootDir).directories.slice(); - } - return this.host.getDirectories(rootDir); + return this.cachedPartialSystem.getDirectories(rootDir); } readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[] { - if (this.canWorkWithCacheForDir(rootDir)) { - return matchFiles(rootDir, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, path => this.getFileSystemEntries(path)); - } - return this.host.readDirectory(rootDir, extensions, excludes, includes, depth); + return this.cachedPartialSystem.readDirectory(rootDir, extensions, excludes, includes, depth); } fileExists(fileName: string): boolean { - const path = this.toPath(fileName); - const result = this.cachedReadDirectoryResult.get(getDirectoryPath(path)); - const baseName = getBaseFileName(toNormalizedPath(fileName)); - return (result && this.hasEntry(result.files, baseName)) || this.host.fileExists(fileName); + return this.cachedPartialSystem.fileExists(fileName); } directoryExists(dirPath: string) { - const path = this.toPath(dirPath); - return this.cachedReadDirectoryResult.has(path) || this.host.directoryExists(dirPath); + return this.cachedPartialSystem.directoryExists(dirPath); } readFile(path: string, encoding?: string): string { return this.host.readFile(path, encoding); } - private fileNameEqual(name1: string, name2: string) { - return this.getCanonicalFileName(name1) === this.getCanonicalFileName(name2); + addOrDeleteFileOrFolder(fileOrFolder: NormalizedPath, fileOrFolderPath: Path) { + return this.cachedPartialSystem.addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath); } - private hasEntry(entries: ReadonlyArray, name: string) { - return some(entries, file => this.fileNameEqual(file, name)); - } - - private updateFileSystemEntry(entries: ReadonlyArray, baseName: string, isValid: boolean) { - if (this.hasEntry(entries, baseName)) { - if (!isValid) { - return filter(entries, entry => !this.fileNameEqual(entry, baseName)); - } - } - else if (isValid) { - return entries.concat(baseName); - } - return entries; - } - - addOrDeleteFileOrFolder(fileOrFolder: NormalizedPath) { - const path = this.toPath(fileOrFolder); - const existingResult = this.cachedReadDirectoryResult.get(path); - if (existingResult) { - // This was a folder already present, remove it if this doesnt exist any more - if (!this.host.directoryExists(fileOrFolder)) { - this.cachedReadDirectoryResult.delete(path); - } - } - else { - // This was earlier a file (hence not in cached directory contents) - // or we never cached the directory containing it - const parentResult = this.cachedReadDirectoryResult.get(getDirectoryPath(path)); - if (parentResult) { - const baseName = getBaseFileName(fileOrFolder); - parentResult.files = this.updateFileSystemEntry(parentResult.files, baseName, this.host.fileExists(path)); - parentResult.directories = this.updateFileSystemEntry(parentResult.directories, baseName, this.host.directoryExists(path)); - } - } + addOrDeleteFile(file: string, path: Path, eventKind: FileWatcherEventKind) { + return this.cachedPartialSystem.addOrDeleteFile(file, path, eventKind); } clearCache() { - this.cachedReadDirectoryResult = createMap(); + return this.cachedPartialSystem.clearCache(); } setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]) { @@ -190,17 +108,16 @@ namespace ts.server { } - type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean }; export class LSHost implements LanguageServiceHost, ModuleResolutionHost { - private compilationSettings: CompilerOptions; - private readonly resolvedModuleNames = createMap>(); - private readonly resolvedTypeReferenceDirectives = createMap>(); + /*@internal*/ + compilationSettings: CompilerOptions; - private filesWithChangedSetOfUnresolvedImports: Path[]; - - private resolveModuleName: typeof resolveModuleName; readonly trace: (s: string) => void; readonly realpath?: (path: string) => string; + + /*@internal*/ + hasInvalidatedResolution: HasInvalidatedResolution; + /** * This is the host that is associated with the project. This is normally same as projectService's host * except in Configured projects where it is CachedServerHost so that we can cache the results of the @@ -217,25 +134,6 @@ namespace ts.server { this.trace = s => host.trace(s); } - this.resolveModuleName = (moduleName, containingFile, compilerOptions, host) => { - const globalCache = this.project.getTypeAcquisition().enable - ? this.project.projectService.typingsInstaller.globalTypingsCacheLocation - : undefined; - const primaryResult = resolveModuleName(moduleName, containingFile, compilerOptions, host); - // return result immediately only if it is .ts, .tsx or .d.ts - if (!isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension)) && globalCache !== undefined) { - // otherwise try to load typings from @types - - // create different collection of failed lookup locations for second pass - // if it will fail and we've already found something during the first pass - we don't want to pollute its results - const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, this.project.getProjectName(), compilerOptions, host, globalCache); - if (resolvedModule) { - return { resolvedModule, failedLookupLocations: primaryResult.failedLookupLocations.concat(failedLookupLocations) }; - } - } - return primaryResult; - }; - if (this.host.realpath) { this.realpath = path => this.host.realpath(path); } @@ -243,99 +141,9 @@ namespace ts.server { dispose() { this.project = undefined; - this.resolveModuleName = undefined; this.host = undefined; } - public startRecordingFilesWithChangedResolutions() { - this.filesWithChangedSetOfUnresolvedImports = []; - } - - public finishRecordingFilesWithChangedResolutions() { - const collected = this.filesWithChangedSetOfUnresolvedImports; - this.filesWithChangedSetOfUnresolvedImports = undefined; - return collected; - } - - private resolveNamesWithLocalCache( - names: string[], - containingFile: string, - cache: Map>, - loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T, - getResult: (s: T) => R, - getResultFileName: (result: R) => string | undefined, - logChanges: boolean): R[] { - - const path = this.project.projectService.toPath(containingFile); - const currentResolutionsInFile = cache.get(path); - - const newResolutions: Map = createMap(); - const resolvedModules: R[] = []; - const compilerOptions = this.getCompilationSettings(); - - for (const name of names) { - // check if this is a duplicate entry in the list - let resolution = newResolutions.get(name); - if (!resolution) { - const existingResolution = currentResolutionsInFile && currentResolutionsInFile.get(name); - if (moduleResolutionIsValid(existingResolution)) { - // ok, it is safe to use existing name resolution results - resolution = existingResolution; - } - else { - resolution = loader(name, containingFile, compilerOptions, this); - newResolutions.set(name, resolution); - } - if (logChanges && this.filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) { - this.filesWithChangedSetOfUnresolvedImports.push(path); - // reset log changes to avoid recording the same file multiple times - logChanges = false; - } - } - - Debug.assert(resolution !== undefined); - - resolvedModules.push(getResult(resolution)); - } - - // replace old results with a new one - cache.set(path, newResolutions); - return resolvedModules; - - function resolutionIsEqualTo(oldResolution: T, newResolution: T): boolean { - if (oldResolution === newResolution) { - return true; - } - if (!oldResolution || !newResolution || oldResolution.isInvalidated) { - return false; - } - const oldResult = getResult(oldResolution); - const newResult = getResult(newResolution); - if (oldResult === newResult) { - return true; - } - if (!oldResult || !newResult) { - return false; - } - return getResultFileName(oldResult) === getResultFileName(newResult); - } - - function moduleResolutionIsValid(resolution: T): boolean { - if (!resolution || resolution.isInvalidated) { - return false; - } - - const result = getResult(resolution); - if (result) { - return true; - } - - // consider situation if we have no candidate locations as valid resolution. - // after all there is no point to invalidate it if we have no idea where to look for the module. - return resolution.failedLookupLocations.length === 0; - } - } - getNewLine() { return this.host.newLine; } @@ -357,13 +165,11 @@ namespace ts.server { } resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] { - return this.resolveNamesWithLocalCache(typeDirectiveNames, containingFile, this.resolvedTypeReferenceDirectives, resolveTypeReferenceDirective, - m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName, /*logChanges*/ false); + return this.project.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile); } resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModuleFull[] { - return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, this.resolveModuleName, - m => m.resolvedModule, r => r.resolvedFileName, /*logChanges*/ true); + return this.project.resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ true); } getDefaultLibFileName() { @@ -426,44 +232,5 @@ namespace ts.server { getDirectories(path: string): string[] { return this.host.getDirectories(path); } - - notifyFileRemoved(info: ScriptInfo) { - this.invalidateResolutionOfDeletedFile(info, this.resolvedModuleNames, - m => m.resolvedModule, r => r.resolvedFileName); - this.invalidateResolutionOfDeletedFile(info, this.resolvedTypeReferenceDirectives, - m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName); - } - - private invalidateResolutionOfDeletedFile( - deletedInfo: ScriptInfo, - cache: Map>, - getResult: (s: T) => R, - getResultFileName: (result: R) => string | undefined) { - cache.forEach((value, path) => { - if (path === deletedInfo.path) { - cache.delete(path); - } - else if (value) { - value.forEach((resolution) => { - if (resolution && !resolution.isInvalidated) { - const result = getResult(resolution); - if (result) { - if (getResultFileName(result) === deletedInfo.path) { - resolution.isInvalidated = true; - } - } - } - }); - } - }); - } - - setCompilationSettings(opt: CompilerOptions) { - if (changesAffectModuleResolution(this.compilationSettings, opt)) { - this.resolvedModuleNames.clear(); - this.resolvedTypeReferenceDirectives.clear(); - } - this.compilationSettings = opt; - } } } diff --git a/src/server/project.ts b/src/server/project.ts index 5c199c2d81..cce0fc5b51 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -3,7 +3,7 @@ /// /// /// -/// +/// namespace ts.server { @@ -127,10 +127,13 @@ namespace ts.server { public languageServiceEnabled = true; + /*@internal*/ + resolutionCache: ResolutionCache; + /*@internal*/ lsHost: LSHost; - builder: Builder; + private builder: Builder; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -210,7 +213,16 @@ namespace ts.server { this.setInternalCompilerOptionsForEmittingJsFiles(); this.lsHost = new LSHost(host, this, this.projectService.cancellationToken); - this.lsHost.setCompilationSettings(this.compilerOptions); + this.resolutionCache = createResolutionCache( + fileName => this.projectService.toPath(fileName), + () => this.compilerOptions, + (failedLookupLocation, failedLookupLocationPath, containingFile, name) => this.watchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath, containingFile, name), + s => this.projectService.logger.info(s), + this.getProjectName(), + () => this.getTypeAcquisition().enable ? this.projectService.typingsInstaller.globalTypingsCacheLocation : undefined + ); + this.lsHost.compilationSettings = this.compilerOptions; + this.resolutionCache.setModuleResolutionHost(this.lsHost); this.languageService = createLanguageService(this.lsHost, this.documentRegistry); @@ -218,10 +230,22 @@ namespace ts.server { this.disableLanguageService(); } - this.builder = createBuilder(this); this.markAsDirty(); } + private watchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) { + // There is some kind of change in the failed lookup location, update the program + return this.projectService.addFileWatcher(WatchType.FailedLookupLocation, this, failedLookupLocation, (fileName, eventKind) => { + this.projectService.logger.info(`Watcher: FailedLookupLocations: Status: ${FileWatcherEventKind[eventKind]}: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name}`); + if (this.projectKind === ProjectKind.Configured) { + (this.lsHost.host as CachedServerHost).addOrDeleteFile(fileName, failedLookupLocationPath, eventKind); + } + this.resolutionCache.invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath); + this.markAsDirty(); + this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this); + }); + } + private setInternalCompilerOptionsForEmittingJsFiles() { if (this.projectKind === ProjectKind.Inferred || this.projectKind === ProjectKind.External) { this.compilerOptions.noEmitForJsFiles = true; @@ -246,12 +270,47 @@ namespace ts.server { return this.languageService; } + private ensureBuilder() { + if (!this.builder) { + this.builder = createBuilder( + this.projectService.toCanonicalFileName, + (_program, sourceFile, emitOnlyDts, isDetailed) => this.getFileEmitOutput(sourceFile, emitOnlyDts, isDetailed), + data => this.projectService.host.createHash(data), + sourceFile => !this.projectService.getScriptInfoForPath(sourceFile.path).hasMixedContent + ); + } + } + getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] { if (!this.languageServiceEnabled) { return []; } this.updateGraph(); - return this.builder.getFilesAffectedBy(scriptInfo); + this.ensureBuilder(); + return this.builder.getFilesAffectedBy(this.program, scriptInfo.path); + } + + /** + * Returns true if emit was conducted + */ + emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean { + this.ensureBuilder(); + const { emitSkipped, outputFiles } = this.builder.emitFile(this.program, scriptInfo.path); + if (!emitSkipped) { + const projectRootPath = this.getProjectRootPath(); + for (const outputFile of outputFiles) { + const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, projectRootPath ? projectRootPath : getDirectoryPath(scriptInfo.fileName)); + writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark); + } + } + + return !emitSkipped; + } + + getChangedFiles() { + Debug.assert(this.languageServiceEnabled); + this.ensureBuilder(); + return this.builder.getChangedProgramFiles(this.program); } getProjectVersion() { @@ -320,6 +379,8 @@ namespace ts.server { this.rootFilesMap = undefined; this.program = undefined; this.builder = undefined; + this.resolutionCache.clear(); + this.resolutionCache = undefined; this.cachedUnresolvedImportsPerFile = undefined; this.lsHost.dispose(); this.lsHost = undefined; @@ -337,6 +398,10 @@ namespace ts.server { this.languageService = undefined; } + isClosed() { + return this.lsHost === undefined; + } + getCompilerOptions() { return this.compilerOptions; } @@ -391,11 +456,11 @@ namespace ts.server { }); } - getFileEmitOutput(info: ScriptInfo, emitOnlyDtsFiles: boolean) { + private getFileEmitOutput(sourceFile: SourceFile, emitOnlyDtsFiles: boolean, isDetailed: boolean) { if (!this.languageServiceEnabled) { return undefined; } - return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles); + return this.getLanguageService().getEmitOutput(sourceFile.fileName, emitOnlyDtsFiles, isDetailed); } getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean) { @@ -454,21 +519,6 @@ namespace ts.server { return false; } - getAllEmittableFiles() { - if (!this.languageServiceEnabled) { - return []; - } - const defaultLibraryFileName = getDefaultLibFileName(this.compilerOptions); - const infos = this.getScriptInfos(); - const result: string[] = []; - for (const info of infos) { - if (getBaseFileName(info.fileName) !== defaultLibraryFileName && shouldEmitFile(info)) { - result.push(info.fileName); - } - } - return result; - } - containsScriptInfo(info: ScriptInfo): boolean { return this.isRoot(info) || (this.program && this.program.getSourceFileByPath(info.path) !== undefined); } @@ -505,7 +555,7 @@ namespace ts.server { if (this.isRoot(info)) { this.removeRoot(info); } - this.lsHost.notifyFileRemoved(info); + this.resolutionCache.invalidateResolutionOfFile(info.path); this.cachedUnresolvedImportsPerFile.remove(info.path); if (detachFromProject) { @@ -560,11 +610,12 @@ namespace ts.server { * @returns: true if set of files in the project stays the same and false - otherwise. */ updateGraph(): boolean { - this.lsHost.startRecordingFilesWithChangedResolutions(); + this.resolutionCache.startRecordingFilesWithChangedResolutions(); + this.lsHost.hasInvalidatedResolution = this.resolutionCache.createHasInvalidatedResolution(); let hasChanges = this.updateGraphWorker(); - const changedFiles: ReadonlyArray = this.lsHost.finishRecordingFilesWithChangedResolutions() || emptyArray; + const changedFiles: ReadonlyArray = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray; for (const file of changedFiles) { // delete cached information for changed files @@ -594,11 +645,14 @@ namespace ts.server { // update builder only if language service is enabled // otherwise tell it to drop its internal state - if (this.languageServiceEnabled) { - this.builder.onProjectUpdateGraph(); - } - else { - this.builder.clear(); + // Note we are retaining builder so we can send events for project change + if (this.builder) { + if (this.languageServiceEnabled) { + this.builder.onProgramUpdateGraph(this.program, this.lsHost.hasInvalidatedResolution); + } + else { + this.builder.clear(); + } } if (hasChanges) { @@ -639,41 +693,15 @@ namespace ts.server { } } - const missingFilePaths = this.program.getMissingFilePaths(); - const newMissingFilePathMap = arrayToSet(missingFilePaths); // Update the missing file paths watcher - mutateMap( + updateMissingFilePathsWatch( + this.program, this.missingFilesMap || (this.missingFilesMap = createMap()), - newMissingFilePathMap, - { - // Watch the missing files - createNewValue: missingFilePath => { - const fileWatcher = this.projectService.addFileWatcher( - WatchType.MissingFilePath, this, missingFilePath, - (filename, eventKind) => { - if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) { - this.missingFilesMap.delete(missingFilePath); - this.projectService.closeFileWatcher(WatchType.MissingFilePath, this, missingFilePath, fileWatcher, WatcherCloseReason.FileCreated); - - if (this.projectKind === ProjectKind.Configured) { - const absoluteNormalizedPath = getNormalizedAbsolutePath(filename, getDirectoryPath(missingFilePath)); - (this.lsHost.host as CachedServerHost).addOrDeleteFileOrFolder(toNormalizedPath(absoluteNormalizedPath)); - } - - // When a missing file is created, we should update the graph. - this.markAsDirty(); - this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this); - } - } - ); - return fileWatcher; - }, - // Files that are no longer missing (e.g. because they are no longer required) - // should no longer be watched. - onDeleteExistingValue: (missingFilePath, fileWatcher) => { - this.projectService.closeFileWatcher(WatchType.MissingFilePath, this, missingFilePath, fileWatcher, WatcherCloseReason.NotNeeded); - } - } + // Watch the missing files + missingFilePath => this.addMissingFileWatcher(missingFilePath), + // Files that are no longer missing (e.g. because they are no longer required) + // should no longer be watched. + (missingFilePath, fileWatcher) => this.closeMissingFileWatcher(missingFilePath, fileWatcher, WatcherCloseReason.NotNeeded) ); } @@ -684,7 +712,7 @@ namespace ts.server { // by the LSHost for files in the program when the program is retrieved above but // the program doesn't contain external files so this must be done explicitly. inserted => { - const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false); + const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false, this.lsHost.host); scriptInfo.attachToProject(this); }, removed => { @@ -697,12 +725,37 @@ namespace ts.server { return hasChanges; } + private addMissingFileWatcher(missingFilePath: Path) { + const fileWatcher = this.projectService.addFileWatcher( + WatchType.MissingFilePath, this, missingFilePath, + (fileName, eventKind) => { + if (this.projectKind === ProjectKind.Configured) { + (this.lsHost.host as CachedServerHost).addOrDeleteFile(fileName, missingFilePath, eventKind); + } + + if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) { + this.missingFilesMap.delete(missingFilePath); + this.closeMissingFileWatcher(missingFilePath, fileWatcher, WatcherCloseReason.FileCreated); + + // When a missing file is created, we should update the graph. + this.markAsDirty(); + this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this); + } + } + ); + return fileWatcher; + } + + private closeMissingFileWatcher(missingFilePath: Path, fileWatcher: FileWatcher, reason: WatcherCloseReason) { + this.projectService.closeFileWatcher(WatchType.MissingFilePath, this, missingFilePath, fileWatcher, reason); + } + isWatchedMissingFile(path: Path) { return this.missingFilesMap && this.missingFilesMap.has(path); } getScriptInfoLSHost(fileName: string) { - const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false); + const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this.lsHost.host); if (scriptInfo) { const existingValue = this.rootFilesMap.get(scriptInfo.path); if (existingValue !== undefined && existingValue !== scriptInfo) { @@ -716,7 +769,10 @@ namespace ts.server { } getScriptInfoForNormalizedPath(fileName: NormalizedPath) { - const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false); + const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath( + fileName, /*openedByClient*/ false, /*fileContent*/ undefined, + /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.lsHost.host + ); if (scriptInfo && !scriptInfo.isAttached(this)) { return Errors.ThrowProjectDoesNotContainDocument(fileName, this); } @@ -746,9 +802,13 @@ namespace ts.server { this.cachedUnresolvedImportsPerFile.clear(); this.lastCachedUnresolvedImportsList = undefined; } + const oldOptions = this.compilerOptions; this.compilerOptions = compilerOptions; this.setInternalCompilerOptionsForEmittingJsFiles(); - this.lsHost.setCompilationSettings(compilerOptions); + if (changesAffectModuleResolution(oldOptions, compilerOptions)) { + this.resolutionCache.clear(); + } + this.lsHost.compilationSettings = this.compilerOptions; this.markAsDirty(); } @@ -814,58 +874,6 @@ namespace ts.server { } } - getReferencedFiles(path: Path): Path[] { - if (!this.languageServiceEnabled) { - return []; - } - - const sourceFile = this.getSourceFile(path); - if (!sourceFile) { - return []; - } - // We need to use a set here since the code can contain the same import twice, - // but that will only be one dependency. - // To avoid invernal conversion, the key of the referencedFiles map must be of type Path - const referencedFiles = createMap(); - if (sourceFile.imports && sourceFile.imports.length > 0) { - const checker: TypeChecker = this.program.getTypeChecker(); - for (const importName of sourceFile.imports) { - const symbol = checker.getSymbolAtLocation(importName); - if (symbol && symbol.declarations && symbol.declarations[0]) { - const declarationSourceFile = symbol.declarations[0].getSourceFile(); - if (declarationSourceFile) { - referencedFiles.set(declarationSourceFile.path, true); - } - } - } - } - - const currentDirectory = getDirectoryPath(path); - // Handle triple slash references - if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { - for (const referencedFile of sourceFile.referencedFiles) { - const referencedPath = this.projectService.toPath(referencedFile.fileName, currentDirectory); - referencedFiles.set(referencedPath, true); - } - } - - // Handle type reference directives - if (sourceFile.resolvedTypeReferenceDirectiveNames) { - sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { - if (!resolvedTypeReferenceDirective) { - return; - } - - const fileName = resolvedTypeReferenceDirective.resolvedFileName; - const typeFilePath = this.projectService.toPath(fileName, currentDirectory); - referencedFiles.set(typeFilePath, true); - }); - } - - const allFileNames = arrayFrom(referencedFiles.keys()) as Path[]; - return filter(allFileNames, file => this.lsHost.host.fileExists(file)); - } - // remove a root file from project protected removeRoot(info: ScriptInfo): void { orderedRemoveItem(this.rootFiles, info); @@ -973,11 +981,6 @@ namespace ts.server { } } - interface WildcardDirectoryWatcher { - watcher: FileWatcher; - flags: WatchDirectoryFlags; - } - /** * If a file is opened, the server will look for a tsconfig (or jsconfig) * and if successfull create a ConfiguredProject for it. @@ -1160,30 +1163,19 @@ namespace ts.server { /*@internal*/ watchWildcards(wildcardDirectories: Map) { - mutateMap( + updateWatchingWildcardDirectories( this.directoriesWatchedForWildcards || (this.directoriesWatchedForWildcards = createMap()), wildcardDirectories, - { - // Watcher is same if the recursive flags match - isSameValue: ({ flags: existingFlags }, flags) => existingFlags !== flags, - // Create new watch and recursive info - createNewValue: (directory, flags) => { - return { - watcher: this.projectService.addDirectoryWatcher( - WatchType.WildcardDirectories, this, directory, - path => this.projectService.onFileAddOrRemoveInWatchedDirectoryOfProject(this, path), - flags - ), - flags - }; - }, - // Close existing watch thats not needed any more - onDeleteExistingValue: (directory, wildcardDirectoryWatcher) => - this.closeWildcardDirectoryWatcher(directory, wildcardDirectoryWatcher, WatcherCloseReason.NotNeeded), - // Close existing watch that doesnt match in the recursive flags - onDeleteExistingMismatchValue: (directory, wildcardDirectoryWatcher) => - this.closeWildcardDirectoryWatcher(directory, wildcardDirectoryWatcher, WatcherCloseReason.RecursiveChanged), - } + // Create new directory watcher + (directory, flags) => this.projectService.addDirectoryWatcher( + WatchType.WildcardDirectories, this, directory, + path => this.projectService.onFileAddOrRemoveInWatchedDirectoryOfProject(this, path), + flags + ), + // Close directory watcher + (directory, wildcardDirectoryWatcher, flagsChanged) => this.closeWildcardDirectoryWatcher( + directory, wildcardDirectoryWatcher, flagsChanged ? WatcherCloseReason.RecursiveChanged : WatcherCloseReason.NotNeeded + ) ); } @@ -1214,7 +1206,7 @@ namespace ts.server { path => this.projectService.onTypeRootFileChanged(this, path), WatchDirectoryFlags.None ), // Close existing watch thats not needed any more - onDeleteExistingValue: (directory, watcher) => this.projectService.closeDirectoryWatcher( + onDeleteValue: (directory, watcher) => this.projectService.closeDirectoryWatcher( WatchType.TypeRoot, this, directory, watcher, WatchDirectoryFlags.None, WatcherCloseReason.NotNeeded ) } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index f50c873112..3cc8555032 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2040,6 +2040,29 @@ namespace ts.server.protocol { languageServiceEnabled: boolean; } + export type ProjectChangedEventName = "projectChanged"; + export interface ProjectStructureChangedEvent extends Event { + event: ProjectChangedEventName; + body: ProjectChangedEventBody; + } + + export interface ProjectChangedEventBody { + /** + * Project name that has changes + */ + projectName: string; + + /** + * Minimum set of file names to emit + */ + fileNamesToEmit: string[]; + + /** + * List of files that have changed/added/removed or could have been affected by the changed files + */ + changedFiles: string[]; + } + /** * Arguments for reload request. */ diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 133879a2dc..93781df95c 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -237,7 +237,7 @@ namespace ts.server { detachAllProjects() { for (const p of this.containingProjects) { if (p.projectKind === ProjectKind.Configured) { - (p.lsHost.host as CachedServerHost).addOrDeleteFileOrFolder(this.fileName); + (p.lsHost.host as CachedServerHost).addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted); } const isInfoRoot = p.isRoot(this); // detach is unnecessary since we'll clean the list of containing projects anyways diff --git a/src/server/server.ts b/src/server/server.ts index 0fe37dd8ba..0ccff7c4d2 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -538,7 +538,15 @@ namespace ts.server { fs.stat(watchedFile.fileName, (err: any, stats: any) => { if (err) { - watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Changed); + if (err.code === "ENOENT") { + if (watchedFile.mtime.getTime() !== 0) { + watchedFile.mtime = new Date(0); + watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Deleted); + } + } + else { + watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Changed); + } } else { const oldTime = watchedFile.mtime.getTime(); diff --git a/src/server/session.ts b/src/server/session.ts index 396edb5bb5..4b42aaba96 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -332,14 +332,18 @@ namespace ts.server { private defaultEventHandler(event: ProjectServiceEvent) { switch (event.eventName) { - case ContextEvent: - const { project, fileName } = event.data; - this.projectService.logger.info(`got context event, updating diagnostics for ${fileName}`); - this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], 100)); + case ProjectChangedEvent: + const { project, filesToEmit, changedFiles } = event.data; + this.projectChangedEvent(project, filesToEmit, changedFiles); break; case ConfigFileDiagEvent: - const { triggerFile, configFileName, diagnostics } = event.data; - this.configFileDiagnosticEvent(triggerFile, configFileName, diagnostics); + const { triggerFile, configFileName: configFile, diagnostics } = event.data; + const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true)); + this.event({ + triggerFile, + configFile, + diagnostics: bakedDiags + }, "configFileDiag"); break; case ProjectLanguageServiceStateEvent: { const eventName: protocol.ProjectLanguageServiceStateEventName = "projectLanguageServiceState"; @@ -360,6 +364,24 @@ namespace ts.server { } } + private projectChangedEvent(project: Project, fileNamesToEmit: string[], changedFiles: string[]): void { + this.projectService.logger.info(`got project changed event, updating diagnostics for ${changedFiles}`); + if (changedFiles.length) { + const checkList = this.createCheckList(changedFiles, project); + + // For now only queue error checking for open files. We can change this to include non open files as well + this.errorCheck.startNew(next => this.updateErrorCheck(next, checkList, 100, /*requireOpen*/ true)); + + + // Send project changed event + this.event({ + projectName: project.getProjectName(), + changedFiles, + fileNamesToEmit + }, "projectChanged"); + } + } + public logError(err: Error, cmd: string) { let msg = "Exception on executing command " + cmd; if (err.message) { @@ -381,21 +403,6 @@ namespace ts.server { this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine)); } - public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ReadonlyArray) { - const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true)); - const ev: protocol.ConfigFileDiagnosticEvent = { - seq: 0, - type: "event", - event: "configFileDiag", - body: { - triggerFile, - configFile, - diagnostics: bakedDiags - } - }; - this.send(ev); - } - public event(info: T, eventName: string) { const ev: protocol.Event = { seq: 0, @@ -1197,13 +1204,13 @@ namespace ts.server { } private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): ReadonlyArray { - const info = this.projectService.getScriptInfo(args.file); - const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = []; - + const info = this.projectService.getScriptInfoEnsuringProjectsUptoDate(args.file); if (!info) { return emptyArray; } + const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = []; + // if specified a project, we only return affected file list in this project const projectsToSearch = args.projectFileName ? [this.projectService.findProject(args.projectFileName)] : info.containingProjects; for (const project of projectsToSearch) { @@ -1227,7 +1234,7 @@ namespace ts.server { return false; } const scriptInfo = project.getScriptInfo(file); - return project.builder.emitFile(scriptInfo, (path, data, writeByteOrderMark) => this.host.writeFile(path, data, writeByteOrderMark)); + return project.emitFile(scriptInfo, (path, data, writeByteOrderMark) => this.host.writeFile(path, data, writeByteOrderMark)); } private getSignatureHelpItems(args: protocol.SignatureHelpRequestArgs, simplifiedResult: boolean): protocol.SignatureHelpItems | SignatureHelpItems { @@ -1257,13 +1264,16 @@ namespace ts.server { } } - private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void { - const checkList = mapDefined(fileNames, uncheckedFileName => { + private createCheckList(fileNames: string[], defaultProject?: Project): PendingErrorCheck[] { + return mapDefined(fileNames, uncheckedFileName => { const fileName = toNormalizedPath(uncheckedFileName); - const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true); + const project = defaultProject || this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true); return project && { fileName, project }; }); + } + private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void { + const checkList = this.createCheckList(fileNames); if (checkList.length > 0) { this.updateErrorCheck(next, checkList, delay); } diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 46c87b2349..3ae1dd1d71 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -304,59 +304,4 @@ namespace ts.server { deleted(oldItems[oldIndex++]); } } - - /** - * clears already present map by calling onDeleteExistingValue callback before deleting that key/value - */ - export function clearMap(map: Map, onDeleteExistingValue: (key: string, existingValue: T) => void) { - // Remove all - map.forEach((existingValue, key) => { - onDeleteExistingValue(key, existingValue); - }); - map.clear(); - } - - export interface MutateMapOptions { - createNewValue(key: string, valueInNewMap: U): T; - onDeleteExistingValue(key: string, existingValue: T, isNotSame?: boolean): void; - - isSameValue?(existingValue: T, valueInNewMap: U): boolean; - onDeleteExistingMismatchValue?(key: string, existingValue: T): void; - } - - /** - * Mutates the map with newMap such that keys in map will be same as newMap. - */ - export function mutateMap(map: Map, newMap: ReadonlyMap, options: MutateMapOptions) { - // If there are new values update them - if (newMap) { - const { isSameValue, createNewValue, onDeleteExistingValue, onDeleteExistingMismatchValue } = options; - // Needs update - map.forEach((existingValue, key) => { - const valueInNewMap = newMap.get(key); - // Not present any more in new map, remove it - if (valueInNewMap === undefined) { - map.delete(key); - onDeleteExistingValue(key, existingValue); - } - // different value - remove it - else if (isSameValue && !isSameValue(existingValue, valueInNewMap)) { - Debug.assert(!!onDeleteExistingMismatchValue); - map.delete(key); - onDeleteExistingMismatchValue(key, existingValue); - } - }); - - // Add new values that are not already present - newMap.forEach((valueInNewMap, key) => { - if (!map.has(key)) { - // New values - map.set(key, createNewValue(key, valueInNewMap)); - } - }); - } - else { - clearMap(map, options.onDeleteExistingValue); - } - } } diff --git a/src/services/services.ts b/src/services/services.ts index 54fbd5ce14..1939133b6b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -814,18 +814,21 @@ namespace ts { return codefix.getSupportedErrorCodes(); } + // Either it will be file name if host doesnt have file or it will be the host's file information + type CachedHostFileInformation = HostFileInformation | string; + // Cache host information about script Should be refreshed // at each language service public entry point, since we don't know when // the set of scripts handled by the host changes. class HostCache { - private fileNameToEntry: Map; + private fileNameToEntry: Map; private _compilationSettings: CompilerOptions; private currentDirectory: string; constructor(private host: LanguageServiceHost, getCanonicalFileName: (fileName: string) => string) { // script id => script index this.currentDirectory = host.getCurrentDirectory(); - this.fileNameToEntry = createMap(); + this.fileNameToEntry = createMap(); // Initialize the list with the root file names const rootFileNames = host.getScriptFileNames(); @@ -842,7 +845,7 @@ namespace ts { } private createEntry(fileName: string, path: Path) { - let entry: HostFileInformation; + let entry: CachedHostFileInformation; const scriptSnapshot = this.host.getScriptSnapshot(fileName); if (scriptSnapshot) { entry = { @@ -852,36 +855,41 @@ namespace ts { scriptKind: getScriptKind(fileName, this.host) }; } + else { + entry = fileName; + } this.fileNameToEntry.set(path, entry); return entry; } - public getEntryByPath(path: Path): HostFileInformation { + public getEntryByPath(path: Path): CachedHostFileInformation | undefined { return this.fileNameToEntry.get(path); } - public containsEntryByPath(path: Path): boolean { - return this.fileNameToEntry.has(path); + public getHostFileInformation(path: Path): HostFileInformation | undefined { + const entry = this.fileNameToEntry.get(path); + return !isString(entry) ? entry : undefined; } public getOrCreateEntryByPath(fileName: string, path: Path): HostFileInformation { - return this.containsEntryByPath(path) - ? this.getEntryByPath(path) - : this.createEntry(fileName, path); + const info = this.getEntryByPath(path) || this.createEntry(fileName, path); + return isString(info) ? undefined : info; } public getRootFileNames(): string[] { - return this.host.getScriptFileNames(); + return arrayFrom(this.fileNameToEntry.values(), entry => { + return isString(entry) ? entry : entry.hostFileName; + }); } public getVersion(path: Path): string { - const file = this.getEntryByPath(path); + const file = this.getHostFileInformation(path); return file && file.version; } public getScriptSnapshot(path: Path): IScriptSnapshot { - const file = this.getEntryByPath(path); + const file = this.getHostFileInformation(path); return file && file.scriptSnapshot; } } @@ -1108,9 +1116,12 @@ namespace ts { // Get a fresh cache of the host information let hostCache = new HostCache(host, getCanonicalFileName); + const rootFileNames = hostCache.getRootFileNames(); + + const hasInvalidatedResolution: HasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse; // If the program is already up-to-date, we can reuse it - if (programUpToDate()) { + if (isProgramUptoDate(program, rootFileNames, hostCache.compilationSettings(), path => hostCache.getVersion(path), fileExists, hasInvalidatedResolution)) { return; } @@ -1120,18 +1131,7 @@ namespace ts { // the program points to old source files that have been invalidated because of // incremental parsing. - const oldSettings = program && program.getCompilerOptions(); const newSettings = hostCache.compilationSettings(); - const shouldCreateNewSourceFiles = oldSettings && - (oldSettings.target !== newSettings.target || - oldSettings.module !== newSettings.module || - oldSettings.moduleResolution !== newSettings.moduleResolution || - oldSettings.noResolve !== newSettings.noResolve || - oldSettings.jsx !== newSettings.jsx || - oldSettings.allowJs !== newSettings.allowJs || - oldSettings.disableSizeLimit !== oldSettings.disableSizeLimit || - oldSettings.baseUrl !== newSettings.baseUrl || - !equalOwnProperties(oldSettings.paths, newSettings.paths)); // Now create a new compiler const compilerHost: CompilerHost = { @@ -1144,19 +1144,13 @@ namespace ts { getDefaultLibFileName: (options) => host.getDefaultLibFileName(options), writeFile: noop, getCurrentDirectory: () => currentDirectory, - fileExists: (fileName): boolean => { - // stub missing host functionality - const path = toPath(fileName, currentDirectory, getCanonicalFileName); - return hostCache.containsEntryByPath(path) ? - !!hostCache.getEntryByPath(path) : - (host.fileExists && host.fileExists(fileName)); - }, + fileExists, readFile(fileName) { // stub missing host functionality const path = toPath(fileName, currentDirectory, getCanonicalFileName); - if (hostCache.containsEntryByPath(path)) { - const entry = hostCache.getEntryByPath(path); - return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength()); + const entry = hostCache.getEntryByPath(path); + if (entry) { + return isString(entry) ? undefined : entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength()); } return host.readFile && host.readFile(fileName); }, @@ -1165,7 +1159,9 @@ namespace ts { }, getDirectories: path => { return host.getDirectories ? host.getDirectories(path) : []; - } + }, + onReleaseOldSourceFile, + hasInvalidatedResolution }; if (host.trace) { compilerHost.trace = message => host.trace(message); @@ -1181,36 +1177,37 @@ namespace ts { } const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings); - const newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program); - - // Release any files we have acquired in the old program but are - // not part of the new program. - if (program) { - const oldSourceFiles = program.getSourceFiles(); - const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldSettings); - for (const oldSourceFile of oldSourceFiles) { - if (!newProgram.getSourceFile(oldSourceFile.fileName) || shouldCreateNewSourceFiles) { - documentRegistry.releaseDocumentWithKey(oldSourceFile.path, oldSettingsKey); - } - } - } + program = createProgram(rootFileNames, newSettings, compilerHost, program); // hostCache is captured in the closure for 'getOrCreateSourceFile' but it should not be used past this point. // It needs to be cleared to allow all collected snapshots to be released hostCache = undefined; - program = newProgram; - // Make sure all the nodes in the program are both bound, and have their parent // pointers set property. program.getTypeChecker(); return; - function getOrCreateSourceFile(fileName: string): SourceFile { - return getOrCreateSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName)); + function fileExists(fileName: string) { + const path = toPath(fileName, currentDirectory, getCanonicalFileName); + const entry = hostCache.getEntryByPath(path); + return entry ? + !isString(entry) : + (host.fileExists && host.fileExists(fileName)); } - function getOrCreateSourceFileByPath(fileName: string, path: Path): SourceFile { + // Release any files we have acquired in the old program but are + // not part of the new program. + function onReleaseOldSourceFile(oldSourceFile: SourceFile, oldOptions: CompilerOptions) { + const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldOptions); + documentRegistry.releaseDocumentWithKey(oldSourceFile.path, oldSettingsKey); + } + + function getOrCreateSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile { + return getOrCreateSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), languageVersion, onError, shouldCreateNewSourceFile); + } + + function getOrCreateSourceFileByPath(fileName: string, path: Path, _languageVersion: ScriptTarget, _onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile { Debug.assert(hostCache !== undefined); // The program is asking for this file, check first if the host can locate it. // If the host can not locate the file, then it does not exist. return undefined @@ -1223,7 +1220,7 @@ namespace ts { // Check if the language version has changed since we last created a program; if they are the same, // it is safe to reuse the sourceFiles; if not, then the shape of the AST can change, and the oldSourceFile // can not be reused. we have to dump all syntax trees and create new ones. - if (!shouldCreateNewSourceFiles) { + if (!shouldCreateNewSourceFile) { // Check if the old program had this file already const oldSourceFile = program && program.getSourceFileByPath(path); if (oldSourceFile) { @@ -1263,49 +1260,6 @@ namespace ts { // Could not find this file in the old program, create a new SourceFile for it. return documentRegistry.acquireDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } - - function sourceFileUpToDate(sourceFile: SourceFile): boolean { - if (!sourceFile) { - return false; - } - const path = sourceFile.path || toPath(sourceFile.fileName, currentDirectory, getCanonicalFileName); - return sourceFile.version === hostCache.getVersion(path); - } - - function programUpToDate(): boolean { - // If we haven't create a program yet, then it is not up-to-date - if (!program) { - return false; - } - - // If number of files in the program do not match, it is not up-to-date - const rootFileNames = hostCache.getRootFileNames(); - if (program.getSourceFiles().length !== rootFileNames.length) { - return false; - } - - // If any file is not up-to-date, then the whole program is not up-to-date - for (const fileName of rootFileNames) { - if (!sourceFileUpToDate(program.getSourceFile(fileName))) { - return false; - } - } - - const currentOptions = program.getCompilerOptions(); - const newOptions = hostCache.compilationSettings(); - // If the compilation settings do no match, then the program is not up-to-date - if (!compareDataObjects(currentOptions, newOptions)) { - return false; - } - - // If everything matches but the text of config file is changed, - // error locations can change for program options, so update the program - if (currentOptions.configFile && newOptions.configFile) { - return currentOptions.configFile.text === newOptions.configFile.text; - } - - return true; - } } function getProgram(): Program { @@ -1520,23 +1474,12 @@ namespace ts { return ts.NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles); } - function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput { + function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean) { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); - const outputFiles: OutputFile[] = []; - - function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { - outputFiles.push({ name: fileName, writeByteOrderMark, text }); - } - const customTransformers = host.getCustomTransformers && host.getCustomTransformers(); - const emitOutput = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); - - return { - outputFiles, - emitSkipped: emitOutput.emitSkipped - }; + return getFileEmitOutput(program, sourceFile, emitOnlyDtsFiles, isDetailed, cancellationToken, customTransformers); } // Signature help diff --git a/src/services/shims.ts b/src/services/shims.ts index 40d4ff2e93..941acdd6d1 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -527,7 +527,7 @@ namespace ts { if (logPerformance) { const end = timestamp(); logger.log(`${actionDescription} completed in ${end - start} msec`); - if (typeof result === "string") { + if (isString(result)) { let str = result; if (str.length > 128) { str = str.substring(0, 128) + "..."; diff --git a/src/services/transpile.ts b/src/services/transpile.ts index 79a69b886d..000bd124fa 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -139,7 +139,7 @@ namespace ts { const value = options[opt.name]; // Value should be a key of opt.type - if (typeof value === "string") { + if (isString(value)) { // If value is not a string, this will fail options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); } diff --git a/src/services/types.ts b/src/services/types.ts index f4f323b8e9..05715f018b 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -185,6 +185,7 @@ namespace ts { */ resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; + hasInvalidatedResolution?: HasInvalidatedResolution; directoryExists?(directoryName: string): boolean; /* @@ -276,7 +277,7 @@ namespace ts { getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; - getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; + getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed; getProgram(): Program; @@ -694,23 +695,12 @@ namespace ts { autoCollapse: boolean; } - export interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; - } - export const enum OutputFileType { JavaScript, SourceMap, Declaration } - export interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; - } - export const enum EndOfLineState { None, InMultiLineCommentTrivia, diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e8f01bb078..d6b7b30cac 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -979,26 +979,6 @@ namespace ts { return result; } - export function compareDataObjects(dst: any, src: any): boolean { - if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) { - return false; - } - - for (const e in dst) { - if (typeof dst[e] === "object") { - if (!compareDataObjects(dst[e], src[e])) { - return false; - } - } - else if (typeof dst[e] !== "function") { - if (dst[e] !== src[e]) { - return false; - } - } - } - return true; - } - export function isArrayLiteralOrObjectLiteralDestructuringPattern(node: Node) { if (node.kind === SyntaxKind.ArrayLiteralExpression || node.kind === SyntaxKind.ObjectLiteralExpression) {