Enable '--strictNullChecks' (#22088)

* Enable '--strictNullChecks'

* Fix API baselines

* Make sys.getEnvironmentVariable non-nullable

* make properties optional instead of using `| undefined` in thier type

* reportDiagnostics should be required

* Declare firstAccessor as non-nullable

* Make `some` a type guard

* Fix `getEnvironmentVariable` definition in tests

* Pretend transformFlags are always defined

* Fix one more use of sys.getEnvironmentVariable

* `requiredResponse` accepts undefined, remove assertions

* Mark optional properties as optional instead of using `| undefined`

* Mark optional properties as optional instead of using ` | undefined`

* Remove unnecessary null assertions

* Put the bang on the declaration instead of every use

* Make `createMapFromTemplate` require a parameter

* Mark `EmitResult.emittedFiles` and `EmitResult.sourceMaps` as optional

* Plumb through undefined in emitLsit and EmitExpressionList

* `ElementAccessExpression.argumentExpression` can not be `undefined`

* Add overloads for `writeTokenText`

* Make `shouldWriteSeparatingLineTerminator` argument non-nullable

* Make `synthesizedNodeStartsOnNewLine` argument required

* `PropertyAssignment.initializer` cannot be undefined

* Use one `!` at declaration site instead of on every use site

* Capture host in a constant and avoid null assertions

* Remove few more unused assertions

* Update baselines

* Use parameter defaults

* Update baselines

* Fix lint

* Make Symbol#valueDeclaration and Symbol#declarations non-optional to reduce assertions

* Make Node#symbol and Type#symbol non-optional to reduce assertions

* Make `flags` non-nullable to reduce assertions

* Convert some asserts to type guards

* Make `isNonLocalAlias` a type guard

* Add overload for `getSymbolOfNode` for `Declaration`

* Some more `getSymbolOfNode` changes

* Push undefined suppression into `typeToTypeNodeHelper`

* `NodeBuilderContext.tracker` is never `undefined`

* use `Debug.assertDefined`

* Remove unnecessary tag

* Mark `LiteralType.freshType` and `LiteralTupe.regularType` as required
This commit is contained in:
Andy
2018-05-22 14:46:57 -07:00
committed by GitHub
parent 3fe946df78
commit e53e56cf82
167 changed files with 4846 additions and 4735 deletions
+2 -2
View File
@@ -182,8 +182,8 @@ namespace compiler {
}
public getSourceMapRecord(): string | undefined {
if (this.result.sourceMaps && this.result.sourceMaps.length > 0) {
return Harness.SourceMapRecorder.getSourceMapRecord(this.result.sourceMaps, this.program, Array.from(this.js.values()), Array.from(this.dts.values()));
if (this.result!.sourceMaps && this.result!.sourceMaps!.length > 0) {
return Harness.SourceMapRecorder.getSourceMapRecord(this.result!.sourceMaps!, this.program!, Array.from(this.js.values()), Array.from(this.dts.values()));
}
}
+11 -11
View File
@@ -81,7 +81,7 @@ class CompilerBaselineRunner extends RunnerBase {
private runSuite(fileName: string, test?: CompilerFileBasedTest, configuration?: Harness.FileBasedTestConfiguration) {
// Mocha holds onto the closure environment of the describe callback even after the test is done.
// Everything declared here should be cleared out in the "after" callback.
let compilerTest: CompilerTest | undefined;
let compilerTest!: CompilerTest;
before(() => { compilerTest = new CompilerTest(fileName, test && test.payload, configuration); });
it(`Correct errors for ${fileName}`, () => { compilerTest.verifyDiagnostics(); });
it(`Correct module resolution tracing for ${fileName}`, () => { compilerTest.verifyModuleResolution(); });
@@ -89,7 +89,7 @@ class CompilerBaselineRunner extends RunnerBase {
it(`Correct JS output for ${fileName}`, () => { if (this.emit) compilerTest.verifyJavaScriptOutput(); });
it(`Correct Sourcemap output for ${fileName}`, () => { compilerTest.verifySourceMapOutput(); });
it(`Correct type/symbol baselines for ${fileName}`, () => { compilerTest.verifyTypesAndSymbols(); });
after(() => { compilerTest = undefined; });
after(() => { compilerTest = undefined!; });
}
private parseOptions() {
@@ -131,7 +131,7 @@ class CompilerTest {
const rootDir = fileName.indexOf("conformance") === -1 ? "tests/cases/compiler/" : ts.getDirectoryPath(fileName) + "/";
if (testCaseContent === undefined) {
testCaseContent = Harness.TestCaseParser.makeUnitsFromTest(Harness.IO.readFile(fileName), fileName, rootDir);
testCaseContent = Harness.TestCaseParser.makeUnitsFromTest(Harness.IO.readFile(fileName)!, fileName, rootDir);
}
if (configurationOverrides) {
@@ -140,13 +140,13 @@ class CompilerTest {
const units = testCaseContent.testUnitData;
this.harnessSettings = testCaseContent.settings;
let tsConfigOptions: ts.CompilerOptions;
let tsConfigOptions: ts.CompilerOptions | undefined;
this.tsConfigFiles = [];
if (testCaseContent.tsConfig) {
assert.equal(testCaseContent.tsConfig.fileNames.length, 0, `list of files in tsconfig is not currently supported`);
tsConfigOptions = ts.cloneCompilerOptions(testCaseContent.tsConfig.options);
this.tsConfigFiles.push(this.createHarnessTestFile(testCaseContent.tsConfigFileUnitData, rootDir, ts.combinePaths(rootDir, tsConfigOptions.configFilePath)));
this.tsConfigFiles.push(this.createHarnessTestFile(testCaseContent.tsConfigFileUnitData!, rootDir, ts.combinePaths(rootDir, tsConfigOptions.configFilePath!)));
}
else {
const baseUrl = this.harnessSettings.baseUrl;
@@ -156,7 +156,7 @@ class CompilerTest {
}
this.lastUnit = units[units.length - 1];
this.hasNonDtsFiles = ts.forEach(units, unit => !ts.fileExtensionIs(unit.name, ts.Extension.Dts));
this.hasNonDtsFiles = units.some(unit => !ts.fileExtensionIs(unit.name, ts.Extension.Dts));
// We need to assemble the list of input files for the compiler and other related files on the 'filesystem' (ie in a multi-file test)
// If the last file in a test uses require or a triple slash reference we'll assume all other files will be brought in via references,
// otherwise, assume all files are just meant to be in the same compilation session without explicit references to one another.
@@ -179,7 +179,7 @@ class CompilerTest {
if (tsConfigOptions && tsConfigOptions.configFilePath !== undefined) {
tsConfigOptions.configFilePath = ts.combinePaths(rootDir, tsConfigOptions.configFilePath);
tsConfigOptions.configFile.fileName = tsConfigOptions.configFilePath;
tsConfigOptions.configFile!.fileName = tsConfigOptions.configFilePath;
}
this.result = Harness.Compiler.compileFiles(
@@ -194,7 +194,7 @@ class CompilerTest {
public static getConfigurations(file: string): CompilerFileBasedTest {
// also see `parseCompilerTestConfigurations` in tests/webTestServer.ts
const content = Harness.IO.readFile(file);
const content = Harness.IO.readFile(file)!;
const rootDir = file.indexOf("conformance") === -1 ? "tests/cases/compiler/" : ts.getDirectoryPath(file) + "/";
const payload = Harness.TestCaseParser.makeUnitsFromTest(content, file, rootDir);
const settings = Harness.TestCaseParser.extractCompilerSettings(content);
@@ -222,7 +222,7 @@ class CompilerTest {
public verifySourceMapRecord() {
if (this.options.sourceMap || this.options.inlineSourceMap || this.options.declarationMap) {
Harness.Baseline.runBaseline(this.justName.replace(/\.tsx?$/, ".sourcemap.txt"), () => {
const record = utils.removeTestPathPrefixes(this.result.getSourceMapRecord());
const record = utils.removeTestPathPrefixes(this.result.getSourceMapRecord()!);
if ((this.options.noEmitOnError && this.result.diagnostics.length !== 0) || record === undefined) {
// Because of the noEmitOnError option no files are created. We need to return null because baselining isn't required.
/* tslint:disable:no-null-keyword */
@@ -263,8 +263,8 @@ class CompilerTest {
Harness.Compiler.doTypeAndSymbolBaseline(
this.justName,
this.result.program,
this.toBeCompiled.concat(this.otherFiles).filter(file => !!this.result.program.getSourceFile(file.unitName)));
this.result.program!,
this.toBeCompiled.concat(this.otherFiles).filter(file => !!this.result.program!.getSourceFile(file.unitName)));
}
private makeUnitName(name: string, root: string) {
+2 -2
View File
@@ -151,9 +151,9 @@ namespace documents {
return match ? new SourceMap(/*mapFile*/ undefined, new Buffer(match[1], "base64").toString("utf8")) : undefined;
}
public static fromSource(text: string) {
public static fromSource(text: string): SourceMap | undefined {
const url = this.getUrl(text);
return url && this.fromUrl(url);
return url === undefined ? undefined : this.fromUrl(url);
}
public getMappingsForEmittedLine(emittedLine: number): ReadonlyArray<Mapping> | undefined {
+2 -2
View File
@@ -16,7 +16,7 @@ interface UserConfig {
abstract class ExternalCompileRunnerBase extends RunnerBase {
abstract testDir: string;
abstract report(result: ExecResult, cwd: string): string;
abstract report(result: ExecResult, cwd: string): string | null;
enumerateTestFiles() {
return Harness.IO.getDirectories(this.testDir);
}
@@ -48,7 +48,7 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
let cwd = path.join(Harness.IO.getWorkspaceRoot(), cls.testDir, directoryName);
const originalCwd = cwd;
const stdio = isWorker ? "pipe" : "inherit";
let types: string[];
let types: string[] | undefined;
if (fs.existsSync(path.join(cwd, "test.json"))) {
const submoduleDir = path.join(cwd, directoryName);
const reset = cp.spawnSync("git", ["reset", "HEAD", "--hard"], { cwd: submoduleDir, timeout, shell: true, stdio });
+6 -6
View File
@@ -19,7 +19,7 @@ namespace fakes {
public readonly output: string[] = [];
public readonly newLine: string;
public readonly useCaseSensitiveFileNames: boolean;
public exitCode: number;
public exitCode: number | undefined;
private readonly _executingFilePath: string | undefined;
private readonly _env: Record<string, string> | undefined;
@@ -128,7 +128,7 @@ namespace fakes {
public getModifiedTime(path: string) {
const stats = this._getStats(path);
return stats ? stats.mtime : undefined;
return stats ? stats.mtime : undefined!; // TODO: GH#18217
}
public createHash(data: string): string {
@@ -144,8 +144,8 @@ namespace fakes {
}
}
public getEnvironmentVariable(name: string): string | undefined {
return this._env && this._env[name];
public getEnvironmentVariable(name: string): string {
return (this._env && this._env[name])!; // TODO: GH#18217
}
private _getStats(path: string) {
@@ -275,7 +275,7 @@ namespace fakes {
this._outputsMap.set(document.file, this.outputs.length);
this.outputs.push(document);
}
this.outputs[this._outputsMap.get(document.file)] = document;
this.outputs[this._outputsMap.get(document.file)!] = document;
}
public trace(s: string): void {
@@ -332,7 +332,7 @@ namespace fakes {
let fs = this.vfs;
while (fs.shadowRoot) {
try {
const shadowRootStats = fs.shadowRoot.existsSync(canonicalFileName) && fs.shadowRoot.statSync(canonicalFileName);
const shadowRootStats = fs.shadowRoot.existsSync(canonicalFileName) ? fs.shadowRoot.statSync(canonicalFileName) : undefined!; // TODO: GH#18217
if (shadowRootStats.dev !== stats.dev ||
shadowRootStats.ino !== stats.ino ||
shadowRootStats.mtimeMs !== stats.mtimeMs) {
+117 -117
View File
@@ -184,7 +184,7 @@ namespace FourSlash {
private inputFiles = ts.createMap<string>(); // Map between inputFile's fileName and its content for easily looking up when resolving references
private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) {
private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[] | undefined) {
let result = "";
ts.forEach(displayParts, part => {
if (result) {
@@ -204,7 +204,7 @@ namespace FourSlash {
// Add input file which has matched file name with the given reference-file path.
// This is necessary when resolveReference flag is specified
private addMatchedInputFile(referenceFilePath: string, extensions: ReadonlyArray<string>) {
private addMatchedInputFile(referenceFilePath: string, extensions: ReadonlyArray<string> | undefined) {
const inputFiles = this.inputFiles;
const languageServiceAdapterHost = this.languageServiceAdapterHost;
const didAdd = tryAdd(referenceFilePath);
@@ -243,16 +243,16 @@ namespace FourSlash {
compilationOptions.skipDefaultLibCheck = true;
// Initialize the language service with all the scripts
let startResolveFileRef: FourSlashFile;
let startResolveFileRef: FourSlashFile | undefined;
let configFileName: string;
let configFileName: string | undefined;
for (const file of testData.files) {
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
this.inputFiles.set(file.fileName, file.content);
if (isConfig(file)) {
const configJson = ts.parseConfigFileTextToJson(file.fileName, file.content);
if (configJson.config === undefined) {
throw new Error(`Failed to parse test ${file.fileName}: ${configJson.error.messageText}`);
throw new Error(`Failed to parse test ${file.fileName}: ${configJson.error!.messageText}`);
}
// Extend our existing compiler options so that we can also support tsconfig only options
@@ -280,12 +280,12 @@ namespace FourSlash {
const baseDir = ts.normalizePath(ts.getDirectoryPath(configFileName));
const files: vfs.FileSet = { [baseDir]: {} };
this.inputFiles.forEach((data, path) => {
const scriptInfo = new Harness.LanguageService.ScriptInfo(path, undefined, /*isRootFile*/ false);
const scriptInfo = new Harness.LanguageService.ScriptInfo(path, undefined!, /*isRootFile*/ false); // TODO: GH#18217
files[path] = new vfs.File(data, { meta: { scriptInfo } });
});
const fs = new vfs.FileSystem(/*ignoreCase*/ true, { cwd: baseDir, files });
const host = new fakes.ParseConfigHost(fs);
const jsonSourceFile = ts.parseJsonText(configFileName, this.inputFiles.get(configFileName));
const jsonSourceFile = ts.parseJsonText(configFileName, this.inputFiles.get(configFileName)!);
compilationOptions = ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, baseDir, compilationOptions, configFileName).options;
}
@@ -323,7 +323,7 @@ namespace FourSlash {
// Check if no-default-lib flag is false and if so add default library
if (!resolvedResult.isLibFile) {
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
Harness.Compiler.getDefaultLibrarySourceFile()!.text, /*isRootFile*/ false);
}
}
else {
@@ -335,7 +335,7 @@ namespace FourSlash {
});
if (!compilationOptions.noLib) {
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
Harness.Compiler.getDefaultLibrarySourceFile()!.text, /*isRootFile*/ false);
}
}
@@ -393,7 +393,7 @@ namespace FourSlash {
(...args) => args.join("|,|")
);
proxy[key] = (...args: any[]) => memo(
target.languageServiceAdapterHost.getScriptInfo(target.activeFile.fileName).version,
target.languageServiceAdapterHost.getScriptInfo(target.activeFile.fileName)!.version,
target.activeFile.fileName,
target.currentCaretPosition,
target.selectionEnd,
@@ -406,7 +406,7 @@ namespace FourSlash {
}
private getFileContent(fileName: string): string {
const script = this.languageServiceAdapterHost.getScriptInfo(fileName);
const script = this.languageServiceAdapterHost.getScriptInfo(fileName)!;
return script.content;
}
@@ -548,9 +548,9 @@ namespace FourSlash {
}
}
private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker): boolean {
private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number | undefined) => boolean, startMarker: Marker, endMarker?: Marker): boolean {
return this.getDiagnostics(startMarker.fileName).some(({ start, length }) =>
predicate(start, start + length, startMarker.position, endMarker === undefined ? undefined : endMarker.position));
predicate(start!, start! + length!, startMarker.position, endMarker === undefined ? undefined : endMarker.position)); // TODO: GH#18217
}
private printErrorLog(expectErrors: boolean, errors: ts.Diagnostic[]) {
@@ -562,12 +562,12 @@ namespace FourSlash {
}
for (const { start, length, messageText, file } of errors) {
Harness.IO.log(" " + this.formatRange(file, start, length) +
Harness.IO.log(" " + this.formatRange(file, start!, length!) + // TODO: GH#18217
", message: " + ts.flattenDiagnosticMessageText(messageText, Harness.IO.newLine()) + "\n");
}
}
private formatRange(file: ts.SourceFile, start: number, length: number) {
private formatRange(file: ts.SourceFile | undefined, start: number, length: number) {
if (file) {
return `from: ${this.formatLineAndCharacterOfPosition(file, start)}, to: ${this.formatLineAndCharacterOfPosition(file, start + length)}`;
}
@@ -597,7 +597,7 @@ namespace FourSlash {
if (errors.length) {
this.printErrorLog(/*expectErrors*/ false, errors);
const error = errors[0];
this.raiseError(`Found an error: ${this.formatPosition(error.file, error.start)}: ${error.messageText}`);
this.raiseError(`Found an error: ${this.formatPosition(error.file!, error.start!)}: ${error.messageText}`);
}
});
}
@@ -635,11 +635,11 @@ namespace FourSlash {
}
private getGoToDefinition(): ts.DefinitionInfo[] {
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
}
private getGoToDefinitionAndBoundSpan(): ts.DefinitionInfoAndBoundSpan {
return this.languageService.getDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition);
return this.languageService.getDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition)!;
}
public verifyGoToType(arg0: any, endMarkerNames?: ArrayOrSingle<string>) {
@@ -694,9 +694,9 @@ namespace FourSlash {
testName = "goToDefinitions";
}
else {
this.verifyDefinitionTextSpan(defs, startMarkerName);
this.verifyDefinitionTextSpan(defs, startMarkerName!);
definitions = defs.definitions;
definitions = defs.definitions!; // TODO: GH#18217
testName = "goToDefinitionsAndBoundSpan";
}
@@ -713,7 +713,7 @@ namespace FourSlash {
}
private verifyDefinitionTextSpan(defs: ts.DefinitionInfoAndBoundSpan, startMarkerName: string) {
const range = this.testData.ranges.find(range => this.markerName(range.marker) === startMarkerName);
const range = this.testData.ranges.find(range => this.markerName(range.marker!) === startMarkerName);
if (!range && !defs.textSpan) {
return;
@@ -791,7 +791,7 @@ namespace FourSlash {
return;
}
const entries = this.getCompletionListAtCaret().entries;
const entries = this.getCompletionListAtCaret()!.entries;
assert.isTrue(items.length <= entries.length, `Amount of expected items in completion list [ ${items.length} ] is greater than actual number of items in list [ ${entries.length} ]`);
ts.zipWith(entries, items, (entry, item) => {
assert.equal(entry.name, item, `Unexpected item in completion list`);
@@ -799,7 +799,7 @@ namespace FourSlash {
}
public noItemsWithSameNameButDifferentKind(): void {
const completions = this.getCompletionListAtCaret();
const completions = this.getCompletionListAtCaret()!;
const uniqueItems = ts.createMap<string>();
for (const item of completions.entries) {
const uniqueItem = uniqueItems.get(item.name);
@@ -854,7 +854,7 @@ namespace FourSlash {
}
private verifyCompletionsWorker(options: FourSlashInterface.VerifyCompletionsOptions): void {
const actualCompletions = this.getCompletionListAtCaret({ ...options.preferences, triggerCharacter: options.triggerCharacter });
const actualCompletions = this.getCompletionListAtCaret({ ...options.preferences, triggerCharacter: options.triggerCharacter })!;
if (!actualCompletions) {
if (options.exact === undefined) return;
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
@@ -877,7 +877,7 @@ namespace FourSlash {
if ("exact" in options) {
ts.Debug.assert(!("includes" in options) && !("excludes" in options));
if (options.exact === undefined) this.raiseError("Expected no completions");
if (options.exact === undefined) throw this.raiseError("Expected no completions");
this.verifyCompletionsAreExactly(actualCompletions.entries, toArray(options.exact));
}
else {
@@ -885,7 +885,7 @@ namespace FourSlash {
for (const include of toArray(options.includes)) {
const name = typeof include === "string" ? include : include.name;
const found = actualByName.get(name);
if (!found) this.raiseError(`No completion ${name} found`);
if (!found) throw this.raiseError(`No completion ${name} found`);
this.verifyCompletionEntry(found, include);
}
}
@@ -929,7 +929,7 @@ namespace FourSlash {
assert.equal(actual.isRecommended, isRecommended);
if (text) {
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source);
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!;
assert.equal(ts.displayPartsToString(actualDetails.displayParts), text);
assert.equal(ts.displayPartsToString(actualDetails.documentation), documentation || "");
// TODO: GH#23587
@@ -962,8 +962,7 @@ namespace FourSlash {
exact: expected,
isNewIdentifierLocation: options && options.isNewIdentifierLocation,
preferences: options,
// TODO: GH#20090
triggerCharacter: (options && options.triggerCharacter) as ts.CompletionsTriggerCharacter | undefined,
triggerCharacter: options && options.triggerCharacter,
});
}
@@ -989,7 +988,7 @@ namespace FourSlash {
* @param spanIndex the index of the range that the completion item's replacement text span should match
*/
public verifyCompletionListDoesNotContain(entryId: ts.Completions.CompletionEntryIdentifier, expectedText?: string, expectedDocumentation?: string, expectedKind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, options?: FourSlashInterface.CompletionsAtOptions) {
let replacementSpan: ts.TextSpan;
let replacementSpan: ts.TextSpan | undefined;
if (spanIndex !== undefined) {
replacementSpan = this.getTextSpanForRangeAtIndex(spanIndex);
}
@@ -1022,7 +1021,7 @@ namespace FourSlash {
// then these symbols must meet the criterion for Not supposed to be in the list. So we
// raise an error
let error = `Completion list did contain '${JSON.stringify(entryId)}\'.`;
const details = this.getCompletionEntryDetails(filterCompletions[0].name);
const details = this.getCompletionEntryDetails(filterCompletions[0].name)!;
if (expectedText) {
error += "Expected text: " + expectedText + " to equal: " + ts.displayPartsToString(details.displayParts) + ".";
}
@@ -1045,7 +1044,7 @@ namespace FourSlash {
}
public verifyCompletionEntryDetails(entryName: string, expectedText: string, expectedDocumentation?: string, kind?: string, tags?: ts.JSDocTagInfo[]) {
const details = this.getCompletionEntryDetails(entryName);
const details = this.getCompletionEntryDetails(entryName)!;
assert(details, "no completion entry available");
@@ -1060,8 +1059,8 @@ namespace FourSlash {
}
if (tags !== undefined) {
assert.equal(details.tags.length, tags.length, this.messageAtLastKnownMarker("QuickInfo tags"));
ts.zipWith(tags, details.tags, (expectedTag, actualTag) => {
assert.equal(details.tags!.length, tags.length, this.messageAtLastKnownMarker("QuickInfo tags"));
ts.zipWith(tags, details.tags!, (expectedTag, actualTag) => {
assert.equal(actualTag.name, expectedTag.name);
assert.equal(actualTag.text, expectedTag.text, this.messageAtLastKnownMarker("QuickInfo tag " + actualTag.name));
});
@@ -1074,7 +1073,7 @@ namespace FourSlash {
private _checker: ts.TypeChecker;
private getProgram(): ts.Program {
return this._program || (this._program = this.languageService.getProgram());
return this._program || (this._program = this.languageService.getProgram()!); // TODO: GH#18217
}
private getChecker() {
@@ -1122,7 +1121,7 @@ namespace FourSlash {
public verifySymbolAtLocation(startRange: Range, declarationRanges: Range[]): void {
const node = this.goToAndGetNode(startRange);
const symbol = this.getChecker().getSymbolAtLocation(node);
const symbol = this.getChecker().getSymbolAtLocation(node)!;
if (!symbol) {
this.raiseError("Could not get symbol at location");
}
@@ -1153,14 +1152,14 @@ namespace FourSlash {
const startFile = this.activeFile.fileName;
for (const fileName of files) {
const searchFileNames = startFile === fileName ? [startFile] : [startFile, fileName];
const highlights = this.getDocumentHighlightsAtCurrentPosition(searchFileNames);
const highlights = this.getDocumentHighlightsAtCurrentPosition(searchFileNames)!;
if (!highlights.every(dh => ts.contains(searchFileNames, dh.fileName))) {
this.raiseError(`When asking for document highlights only in files ${searchFileNames}, got document highlights in ${unique(highlights, dh => dh.fileName)}`);
}
}
}
public verifyReferenceGroups(starts: ArrayOrSingle<string> | ArrayOrSingle<Range>, parts: ReadonlyArray<FourSlashInterface.ReferenceGroup> | undefined): void {
public verifyReferenceGroups(starts: ArrayOrSingle<string> | ArrayOrSingle<Range>, parts: ReadonlyArray<FourSlashInterface.ReferenceGroup>): void {
interface ReferenceGroupJson {
definition: string | { text: string, range: ts.TextSpan };
references: ts.ReferenceEntry[];
@@ -1220,7 +1219,7 @@ namespace FourSlash {
// Necessary to have this function since `findReferences` isn't implemented in `client.ts`
public verifyGetReferencesForServerTest(expected: ReadonlyArray<ts.ReferenceEntry>): void {
const refs = this.getReferencesAtCaret();
assert.deepEqual(refs, expected);
assert.deepEqual<ReadonlyArray<ts.ReferenceEntry> | undefined>(refs, expected);
}
public verifySingleReferenceGroup(definition: FourSlashInterface.ReferenceGroupDefinition, ranges?: Range[]) {
@@ -1274,7 +1273,7 @@ Actual: ${stringify(fullActual)}`);
}
public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) {
const referencedSymbols = this.findReferencesAtCaret();
const referencedSymbols = this.findReferencesAtCaret()!;
if (referencedSymbols.length === 0) {
this.raiseError("No referenced symbols found at current caret position");
@@ -1287,11 +1286,11 @@ Actual: ${stringify(fullActual)}`);
TestState.getDisplayPartsJson(expected), this.messageAtLastKnownMarker("referenced symbol definition display parts"));
}
private getCompletionListAtCaret(options?: ts.GetCompletionsAtPositionOptions): ts.CompletionInfo {
private getCompletionListAtCaret(options?: ts.GetCompletionsAtPositionOptions): ts.CompletionInfo | undefined {
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options);
}
private getCompletionEntryDetails(entryName: string, source?: string, preferences?: ts.UserPreferences): ts.CompletionEntryDetails {
private getCompletionEntryDetails(entryName: string, source?: string, preferences?: ts.UserPreferences): ts.CompletionEntryDetails | undefined {
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, preferences);
}
@@ -1366,14 +1365,14 @@ Actual: ${stringify(fullActual)}`);
tags: ts.JSDocTagInfo[]
) {
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind"));
assert.equal(actualQuickInfo.kindModifiers, kindModifiers, this.messageAtLastKnownMarker("QuickInfo kindModifiers"));
assert.equal(JSON.stringify(actualQuickInfo.textSpan), JSON.stringify(textSpan), this.messageAtLastKnownMarker("QuickInfo textSpan"));
assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.displayParts), TestState.getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts"));
assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.documentation), TestState.getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation"));
assert.equal(actualQuickInfo.tags.length, tags.length, this.messageAtLastKnownMarker("QuickInfo tags"));
ts.zipWith(tags, actualQuickInfo.tags, (expectedTag, actualTag) => {
assert.equal(actualQuickInfo.tags!.length, tags.length, this.messageAtLastKnownMarker("QuickInfo tags"));
ts.zipWith(tags, actualQuickInfo.tags!, (expectedTag, actualTag) => {
assert.equal(expectedTag.name, actualTag.name);
assert.equal(expectedTag.text, actualTag.text, this.messageAtLastKnownMarker("QuickInfo tag " + actualTag.name));
});
@@ -1481,10 +1480,10 @@ Actual: ${stringify(fullActual)}`);
}
private verifySignatureHelpWorker(options: FourSlashInterface.VerifySignatureHelpOptions) {
const help = this.getSignatureHelp();
const help = this.getSignatureHelp()!;
const selectedItem = help.items[help.selectedItemIndex];
// Argument index may exceed number of parameters
const currentParameter: ts.SignatureHelpParameter | undefined = selectedItem.parameters[help.argumentIndex];
const currentParameter = selectedItem.parameters[help.argumentIndex] as ts.SignatureHelpParameter | undefined;
assert.equal(help.items.length, options.overloadsCount || 1, this.assertionMessageAtLastKnownMarker("signature help overloads count"));
@@ -1539,7 +1538,7 @@ Actual: ${stringify(fullActual)}`);
}
}
private validate(name: string, expected: string, actual: string) {
private validate(name: string, expected: string | undefined, actual: string | undefined) {
if (expected && expected !== actual) {
this.raiseError("Expected " + name + " '" + expected + "'. Got '" + actual + "' instead.");
}
@@ -1601,19 +1600,19 @@ Actual: ${stringify(fullActual)}`);
let nextLine = 0;
let resultString = "";
let currentLine: string;
let previousSpanInfo: string;
let startColumn: number;
let length: number;
let previousSpanInfo: string | undefined;
let startColumn: number | undefined;
let length: number | undefined;
const prefixString = " >";
let pos = 0;
const addSpanInfoString = () => {
if (previousSpanInfo) {
resultString += currentLine;
let thisLineMarker = ts.repeatString(" ", startColumn) + ts.repeatString("~", length);
let thisLineMarker = ts.repeatString(" ", startColumn!) + ts.repeatString("~", length!);
thisLineMarker += ts.repeatString(" ", this.alignmentForExtraInfo - thisLineMarker.length - prefixString.length + 1);
resultString += thisLineMarker;
resultString += "=> Pos: (" + (pos - length) + " to " + (pos - 1) + ") ";
resultString += "=> Pos: (" + (pos - length!) + " to " + (pos - 1) + ") ";
resultString += " " + previousSpanInfo;
previousSpanInfo = undefined;
}
@@ -1634,12 +1633,12 @@ Actual: ${stringify(fullActual)}`);
if (previousSpanInfo && previousSpanInfo !== spanInfo) {
addSpanInfoString();
previousSpanInfo = spanInfo;
startColumn = startColumn + length;
startColumn = startColumn! + length!;
length = 1;
}
else {
previousSpanInfo = spanInfo;
length++;
length!++;
}
}
addSpanInfoString();
@@ -1660,7 +1659,7 @@ Actual: ${stringify(fullActual)}`);
Harness.Baseline.runBaseline(
baselineFile,
() => {
return this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos));
return this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!);
});
}
@@ -1693,10 +1692,10 @@ Actual: ${stringify(fullActual)}`);
if (emitOutput.emitSkipped) {
resultString += "Diagnostics:" + Harness.IO.newLine();
const diagnostics = ts.getPreEmitDiagnostics(this.languageService.getProgram());
const diagnostics = ts.getPreEmitDiagnostics(this.languageService.getProgram()!); // TODO: GH#18217
for (const diagnostic of diagnostics) {
if (!ts.isString(diagnostic.messageText)) {
let chainedMessage = diagnostic.messageText;
let chainedMessage: ts.DiagnosticMessageChain | undefined = diagnostic.messageText;
let indentation = " ";
while (chainedMessage) {
resultString += indentation + chainedMessage.messageText + Harness.IO.newLine();
@@ -1741,7 +1740,7 @@ Actual: ${stringify(fullActual)}`);
}
public printBreakpointLocation(pos: number) {
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(this.getBreakpointStatementLocation(pos), " "));
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(this.getBreakpointStatementLocation(pos)!, " "));
}
public printBreakpointAtCurrentLocation() {
@@ -1754,8 +1753,8 @@ Actual: ${stringify(fullActual)}`);
}
public printCurrentQuickInfo() {
const quickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
Harness.IO.log("Quick Info: " + quickInfo.displayParts.map(part => part.text).join(""));
const quickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
Harness.IO.log("Quick Info: " + quickInfo.displayParts!.map(part => part.text).join(""));
}
public printErrorList() {
@@ -1790,7 +1789,7 @@ Actual: ${stringify(fullActual)}`);
}
public printCurrentSignatureHelp() {
const help = this.getSignatureHelp();
const help = this.getSignatureHelp()!;
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
}
@@ -1803,7 +1802,7 @@ Actual: ${stringify(fullActual)}`);
this.printMembersOrCompletions(completions);
}
private printMembersOrCompletions(info: ts.CompletionInfo) {
private printMembersOrCompletions(info: ts.CompletionInfo | undefined) {
if (info === undefined) { return "No completion info."; }
const { entries } = info;
@@ -2087,7 +2086,7 @@ Actual: ${stringify(fullActual)}`);
}
public goToTypeDefinition(definitionIndex: number) {
const definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
if (!definitions || !definitions.length) {
this.raiseError("goToTypeDefinition failed - expected to find at least one definition location but got 0");
}
@@ -2130,7 +2129,7 @@ Actual: ${stringify(fullActual)}`);
}
public goToImplementation() {
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
if (!implementations || !implementations.length) {
this.raiseError("goToImplementation failed - expected to find at least one implementation location but got 0");
}
@@ -2145,7 +2144,7 @@ Actual: ${stringify(fullActual)}`);
public verifyRangesInImplementationList(markerName: string) {
this.goToMarker(markerName);
const implementations: ImplementationLocationInformation[] = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const implementations: ImplementationLocationInformation[] = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
if (!implementations || !implementations.length) {
this.raiseError("verifyRangesInImplementationList failed - expected to find at least one implementation location but got 0");
}
@@ -2321,7 +2320,7 @@ Actual: ${stringify(fullActual)}`);
public verifyCurrentNameOrDottedNameSpanText(text: string) {
const span = this.languageService.getNameOrDottedNameSpan(this.activeFile.fileName, this.currentCaretPosition, this.currentCaretPosition);
if (!span) {
this.raiseError("verifyCurrentNameOrDottedNameSpanText\n" +
return this.raiseError("verifyCurrentNameOrDottedNameSpanText\n" +
"\tExpected: \"" + text + "\"\n" +
"\t Actual: undefined");
}
@@ -2343,12 +2342,12 @@ Actual: ${stringify(fullActual)}`);
this.testData.globalOptions[MetadataOptionNames.baselineFile],
() => {
return this.baselineCurrentFileLocations(pos =>
this.getNameOrDottedNameSpan(pos));
this.getNameOrDottedNameSpan(pos)!);
});
}
public printNameOrDottedNameSpans(pos: number) {
Harness.IO.log(this.spanInfoToString(this.getNameOrDottedNameSpan(pos), "**"));
Harness.IO.log(this.spanInfoToString(this.getNameOrDottedNameSpan(pos)!, "**"));
}
private verifyClassifications(expected: { classificationType: string; text: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[], sourceFileText: string) {
@@ -2407,7 +2406,7 @@ Actual: ${stringify(fullActual)}`);
);
assert.equal(
expected.join(","),
actual.fileNames.map(file => {
actual.fileNames!.map(file => {
return file.replace(this.basePath + "/", "");
}).join(",")
);
@@ -2482,18 +2481,19 @@ Actual: ${stringify(fullActual)}`);
public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) {
this.goToMarker(markerName);
const details = this.getCompletionEntryDetails(options.name, options.source, options.preferences);
if (details.codeActions.length !== 1) {
this.raiseError(`Expected one code action, got ${details.codeActions.length}`);
const details = this.getCompletionEntryDetails(options.name, options.source, options.preferences)!;
const codeActions = details.codeActions!;
if (codeActions.length !== 1) {
this.raiseError(`Expected one code action, got ${codeActions.length}`);
}
if (details.codeActions[0].description !== options.description) {
this.raiseError(`Expected description to be:\n${options.description}\ngot:\n${details.codeActions[0].description}`);
if (codeActions[0].description !== options.description) {
this.raiseError(`Expected description to be:\n${options.description}\ngot:\n${codeActions[0].description}`);
}
this.applyCodeActions(details.codeActions);
this.applyCodeActions(codeActions);
this.verifyNewContent(options, ts.flatMap(details.codeActions, a => a.changes.map(c => c.fileName)));
this.verifyNewContent(options, ts.flatMap(codeActions, a => a.changes.map(c => c.fileName)));
}
public verifyRangeIs(expectedText: string, includeWhiteSpace?: boolean) {
@@ -2527,10 +2527,10 @@ Actual: ${stringify(fullActual)}`);
const fixWithId = ts.find(this.getCodeFixes(this.activeFile.fileName), a => a.fixId === fixId);
ts.Debug.assert(fixWithId !== undefined, "No available code fix has that group id.", () =>
`Expected '${fixId}'. Available action ids: ${ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId)}`);
ts.Debug.assertEqual(fixWithId.fixAllDescription, fixAllDescription);
ts.Debug.assertEqual(fixWithId!.fixAllDescription, fixAllDescription);
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultPreferences);
assert.deepEqual(commands, expectedCommands);
assert.deepEqual<ReadonlyArray<{}> | undefined>(commands, expectedCommands);
assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files");
this.applyChanges(changes);
this.verifyCurrentFileContent(newFileContent);
@@ -2601,7 +2601,7 @@ Actual: ${stringify(fullActual)}`);
}
}
else {
this.verifyRangeIs(options.newRangeContent, /*includeWhitespace*/ true);
this.verifyRangeIs(options.newRangeContent!, /*includeWhitespace*/ true);
}
}
@@ -2621,7 +2621,7 @@ Actual: ${stringify(fullActual)}`);
return;
}
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings, preferences);
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start!, diagnostic.start! + diagnostic.length!, [diagnostic.code], this.formatCodeSettings, preferences);
});
}
@@ -2665,7 +2665,7 @@ Actual: ${stringify(fullActual)}`);
}
const actualTextArray: string[] = [];
const scriptInfo = this.languageServiceAdapterHost.getScriptInfo(fileName);
const scriptInfo = this.languageServiceAdapterHost.getScriptInfo(fileName)!;
const originalContent = scriptInfo.content;
for (const codeFix of codeFixes) {
ts.Debug.assert(codeFix.changes.length === 1);
@@ -2688,7 +2688,7 @@ Actual: ${stringify(fullActual)}`);
public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) {
const name = "verifyDocCommentTemplate";
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
if (expected === undefined) {
if (actual) {
@@ -2727,7 +2727,7 @@ Actual: ${stringify(fullActual)}`);
const charCode = openBraceMap.get(openingBrace);
if (!charCode) {
this.raiseError(`Invalid openingBrace '${openingBrace}' specified.`);
throw this.raiseError(`Invalid openingBrace '${openingBrace}' specified.`);
}
const position = this.currentCaretPosition;
@@ -2899,7 +2899,7 @@ Actual: ${stringify(fullActual)}`);
const occurrences = this.getOccurrencesAtCurrentPosition();
if (!occurrences || occurrences.length === 0) {
this.raiseError("verifyOccurrencesAtPositionListContains failed - found 0 references, expected at least one.");
return this.raiseError("verifyOccurrencesAtPositionListContains failed - found 0 references, expected at least one.");
}
for (const occurrence of occurrences) {
@@ -3091,13 +3091,13 @@ Actual: ${stringify(fullActual)}`);
const action = ts.firstDefined(refactorsWithName, refactor => refactor.actions.find(a => a.name === actionName));
if (!action) {
this.raiseError(`The expected action: ${actionName} is not included in: ${ts.flatMap(refactorsWithName, r => r.actions.map(a => a.name))}`);
throw this.raiseError(`The expected action: ${actionName} is not included in: ${ts.flatMap(refactorsWithName, r => r.actions.map(a => a.name))}`);
}
if (action.description !== actionDescription) {
this.raiseError(`Expected action description to be ${JSON.stringify(actionDescription)}, got: ${JSON.stringify(action.description)}`);
}
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.defaultPreferences);
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.defaultPreferences)!;
for (const edit of editInfo.edits) {
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
}
@@ -3144,12 +3144,12 @@ Actual: ${stringify(fullActual)}`);
public moveToNewFile(options: FourSlashInterface.MoveToNewFileOptions): void {
assert(this.getRanges().length === 1);
const range = this.getRanges()[0];
const refactor = ts.find(this.getApplicableRefactors(range, { allowTextChangesInNewFiles: true }), r => r.name === "Move to a new file");
const refactor = ts.find(this.getApplicableRefactors(range, { allowTextChangesInNewFiles: true }), r => r.name === "Move to a new file")!;
assert(refactor.actions.length === 1);
const action = ts.first(refactor.actions);
assert(action.name === "Move to a new file" && action.description === "Move to a new file");
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, ts.defaultPreferences);
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, ts.defaultPreferences)!;
for (const edit of editInfo.edits) {
const newContent = options.newFileContents[edit.fileName];
if (newContent === undefined) {
@@ -3190,7 +3190,7 @@ Actual: ${stringify(fullActual)}`);
this.raiseError(`The expected refactor: ${refactorNameToApply} is not available at the marker location.`);
}
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.defaultPreferences);
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.defaultPreferences)!;
for (const edit of editInfo.edits) {
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
@@ -3260,7 +3260,7 @@ Actual: ${stringify(fullActual)}`);
const item = matchingItems[0];
if (documentation !== undefined || text !== undefined || entryId.source !== undefined) {
const details = this.getCompletionEntryDetails(item.name, item.source);
const details = this.getCompletionEntryDetails(item.name, item.source)!;
if (documentation !== undefined) {
eq(ts.displayPartsToString(details.documentation), documentation, "completion item documentation");
@@ -3348,11 +3348,11 @@ Actual: ${stringify(fullActual)}`);
private getTextSpanForRangeAtIndex(index: number): ts.TextSpan {
const ranges = this.getRanges();
if (ranges && ranges.length > index) {
if (ranges.length > index) {
return ts.createTextSpanFromRange(ranges[index]);
}
else {
this.raiseError("Supplied span index: " + index + " does not exist in range list of size: " + (ranges ? 0 : ranges.length));
throw this.raiseError("Supplied span index: " + index + " does not exist in range list of size: " + ranges.length);
}
}
@@ -3374,8 +3374,8 @@ Actual: ${stringify(fullActual)}`);
this.cancellationToken.resetCancelled();
}
private static textSpansEqual(a: ts.TextSpan, b: ts.TextSpan) {
return a && b && a.start === b.start && a.length === b.length;
private static textSpansEqual(a: ts.TextSpan | undefined, b: ts.TextSpan | undefined): boolean {
return !!a && !!b && a.start === b.start && a.length === b.length;
}
public getEditsForFileRename(options: FourSlashInterface.GetEditsForFileRenameOptions): void {
@@ -3393,7 +3393,7 @@ Actual: ${stringify(fullActual)}`);
}
export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) {
const content = Harness.IO.readFile(fileName);
const content = Harness.IO.readFile(fileName)!;
runFourSlashTestContent(basePath, testType, content, fileName);
}
@@ -3406,8 +3406,8 @@ Actual: ${stringify(fullActual)}`);
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
const state = new TestState(absoluteBasePath, testType, testData);
const output = ts.transpileModule(content, { reportDiagnostics: true });
if (output.diagnostics.length > 0) {
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics[0].messageText}`);
if (output.diagnostics!.length > 0) {
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`);
}
runCode(output.outputText, state);
}
@@ -3558,11 +3558,11 @@ ${code}
return Harness.getConfigNameFromFileName(file.fileName) !== undefined;
}
function getNonFileNameOptionInFileList(files: FourSlashFile[]): string {
function getNonFileNameOptionInFileList(files: FourSlashFile[]): string | undefined {
return ts.forEach(files, f => getNonFileNameOptionInObject(f.fileOptions));
}
function getNonFileNameOptionInObject(optionObject: { [s: string]: string }): string {
function getNonFileNameOptionInObject(optionObject: { [s: string]: string }): string | undefined {
for (const option in optionObject) {
if (option !== MetadataOptionNames.fileName) {
return option;
@@ -3582,7 +3582,7 @@ ${code}
throw new Error(errorMessage);
}
function recordObjectMarker(fileName: string, location: LocationInformation, text: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker {
function recordObjectMarker(fileName: string, location: LocationInformation, text: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker | undefined {
let markerValue: any;
try {
// Attempt to parse the marker value as JSON
@@ -3613,7 +3613,7 @@ ${code}
return marker;
}
function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker {
function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker | undefined {
const marker: Marker = {
fileName,
position: location.position
@@ -3642,7 +3642,7 @@ ${code}
let output = "";
/// The current marker (or maybe multi-line comment?) we're parsing, possibly
let openMarker: LocationInformation;
let openMarker: LocationInformation | undefined;
/// A stack of the open range markers that are still unclosed
const openRanges: RangeLocationInformation[] = [];
@@ -3663,7 +3663,7 @@ ${code}
let line = 1;
let column = 1;
const flush = (lastSafeCharIndex: number) => {
const flush = (lastSafeCharIndex: number | undefined) => {
output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex === undefined ? undefined : lastSafeCharIndex - lastNormalCharPosition);
};
@@ -3690,7 +3690,7 @@ ${code}
// found a range end
const rangeStart = openRanges.pop();
if (!rangeStart) {
reportError(fileName, line, column, "Found range end with no matching start.");
throw reportError(fileName, line, column, "Found range end with no matching start.");
}
const range: Range = {
@@ -3733,8 +3733,8 @@ ${code}
// Object markers are only ever terminated by |} and have no content restrictions
if (previousChar === "|" && currentChar === "}") {
// Record the marker
const objectMarkerNameText = content.substring(openMarker.sourcePosition + 2, i - 1).trim();
const marker = recordObjectMarker(fileName, openMarker, objectMarkerNameText, markerMap, markers);
const objectMarkerNameText = content.substring(openMarker!.sourcePosition + 2, i - 1).trim();
const marker = recordObjectMarker(fileName, openMarker!, objectMarkerNameText, markerMap, markers);
if (openRanges.length > 0) {
openRanges[openRanges.length - 1].marker = marker;
@@ -3742,7 +3742,7 @@ ${code}
// Set the current start to point to the end of the current marker to ignore its text
lastNormalCharPosition = i + 1;
difference += i + 1 - openMarker.sourcePosition;
difference += i + 1 - openMarker!.sourcePosition;
// Reset the state
openMarker = undefined;
@@ -3754,17 +3754,17 @@ ${code}
if (previousChar === "*" && currentChar === "/") {
// Record the marker
// start + 2 to ignore the */, -1 on the end to ignore the * (/ is next)
const markerNameText = content.substring(openMarker.sourcePosition + 2, i - 1).trim();
const marker = recordMarker(fileName, openMarker, markerNameText, markerMap, markers);
const markerNameText = content.substring(openMarker!.sourcePosition + 2, i - 1).trim();
const marker = recordMarker(fileName, openMarker!, markerNameText, markerMap, markers);
if (openRanges.length > 0) {
openRanges[openRanges.length - 1].marker = marker;
}
// Set the current start to point to the end of the current marker to ignore its text
flush(openMarker.sourcePosition);
flush(openMarker!.sourcePosition);
lastNormalCharPosition = i + 1;
difference += i + 1 - openMarker.sourcePosition;
difference += i + 1 - openMarker!.sourcePosition;
// Reset the state
openMarker = undefined;
@@ -3865,7 +3865,7 @@ ${code}
return s.replace(/\s/g, "");
}
function findDuplicatedElement<T>(a: ReadonlyArray<T>, equal: (a: T, b: T) => boolean): T {
function findDuplicatedElement<T>(a: ReadonlyArray<T>, equal: (a: T, b: T) => boolean): T | undefined {
for (let i = 0; i < a.length; i++) {
for (let j = i + 1; j < a.length; j++) {
if (equal(a[i], a[j])) {
@@ -3889,7 +3889,7 @@ namespace FourSlashInterface {
return this.state.getMarkerNames();
}
public marker(name?: string): FourSlash.Marker {
public marker(name: string): FourSlash.Marker {
return this.state.getMarkerByName(name);
}
@@ -3936,7 +3936,7 @@ namespace FourSlashInterface {
public eachMarker(action: (marker: FourSlash.Marker, index: number) => void): void;
public eachMarker(a: ReadonlyArray<string> | ((marker: FourSlash.Marker, index: number) => void), b?: (marker: FourSlash.Marker, index: number) => void): void {
const markers = typeof a === "function" ? this.state.getMarkers() : a.map(m => this.state.getMarkerByName(m));
this.state.goToEachMarker(markers, typeof a === "function" ? a : b);
this.state.goToEachMarker(markers, typeof a === "function" ? a : b!);
}
@@ -4146,7 +4146,7 @@ namespace FourSlashInterface {
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
}
public quickInfoAt(markerName: string, expectedText?: string, expectedDocumentation?: string) {
public quickInfoAt(markerName: string, expectedText: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoAt(markerName, expectedText, expectedDocumentation);
}
@@ -4765,7 +4765,7 @@ namespace FourSlashInterface {
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;
readonly excludes?: ArrayOrSingle<string | { readonly name: string, readonly source: string }>;
readonly preferences: ts.UserPreferences;
readonly preferences?: ts.UserPreferences;
readonly triggerCharacter?: ts.CompletionsTriggerCharacter;
}
+53 -54
View File
@@ -148,7 +148,7 @@ namespace Utils {
path = "tests/" + path;
}
let content: string;
let content: string | undefined;
try {
content = Harness.IO.readFile(Harness.userSpecifiedRoot + path);
}
@@ -177,7 +177,7 @@ namespace Utils {
export const canonicalizeForHarness = ts.createGetCanonicalFileName(/*caseSensitive*/ false); // This is done so tests work on windows _and_ linux
export function assertInvariants(node: ts.Node, parent: ts.Node): void {
export function assertInvariants(node: ts.Node | undefined, parent: ts.Node | undefined): void {
if (node) {
assert.isFalse(node.pos < 0, "node.pos < 0");
assert.isFalse(node.end < 0, "node.end < 0");
@@ -504,11 +504,11 @@ namespace Harness {
newLine(): string;
getCurrentDirectory(): string;
useCaseSensitiveFileNames(): boolean;
resolvePath(path: string): string;
resolvePath(path: string): string | undefined;
getFileSize(path: string): number;
readFile(path: string): string | undefined;
writeFile(path: string, contents: string): void;
directoryName(path: string): string;
directoryName(path: string): string | undefined;
getDirectories(path: string): string[];
createDirectory(path: string): void;
fileExists(fileName: string): boolean;
@@ -525,7 +525,7 @@ namespace Harness {
getAccessibleFileSystemEntries(dirname: string): ts.FileSystemEntries;
tryEnableSourceMapsForHost?(): void;
getEnvironmentVariable?(name: string): string;
getMemoryUsage?(): number;
getMemoryUsage?(): number | undefined;
}
export let IO: IO;
@@ -564,9 +564,7 @@ namespace Harness {
return runner.enumerateTestFiles();
}
function listFiles(path: string, spec: RegExp, options?: { recursive?: boolean }) {
options = options || {};
function listFiles(path: string, spec: RegExp, options: { recursive?: boolean } = {}) {
function filesInFolder(folder: string): string[] {
let paths: string[] = [];
@@ -634,7 +632,7 @@ namespace Harness {
getCurrentDirectory: () => ts.sys.getCurrentDirectory(),
useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
resolvePath: (path: string) => ts.sys.resolvePath(path),
getFileSize: (path: string) => ts.sys.getFileSize(path),
getFileSize: (path: string) => ts.sys.getFileSize!(path),
readFile: path => ts.sys.readFile(path),
writeFile: (path, content) => ts.sys.writeFile(path, content),
directoryName,
@@ -692,7 +690,7 @@ namespace Harness {
}
}
public static combine(left: HttpHeaders | undefined, right: HttpHeaders | undefined): HttpHeaders {
public static combine(left: HttpHeaders | undefined, right: HttpHeaders | undefined): HttpHeaders | undefined {
if (!left && !right) return undefined;
const headers = new HttpHeaders();
if (left) left.forEach((value, key) => { headers.set(key, value); });
@@ -768,8 +766,8 @@ namespace Harness {
public static readResponseContent(xhr: XMLHttpRequest) {
if (typeof xhr.responseText === "string") {
return new HttpContent({
"Content-Type": xhr.getResponseHeader("Content-Type") || undefined,
"Content-Length": xhr.getResponseHeader("Content-Length") || undefined
"Content-Type": xhr.getResponseHeader("Content-Type") || undefined!, // TODO: GH#18217
"Content-Length": xhr.getResponseHeader("Content-Length") || undefined!, // TODO: GH#18217
}, xhr.responseText);
}
return undefined;
@@ -888,7 +886,7 @@ namespace Harness {
function getFileSize(path: string): number {
const response = send(HttpRequestMessage.head(new URL(path, serverRoot)));
return HttpResponseMessage.hasSuccessStatusCode(response) ? +response.headers.get("Content-Length").toString() : 0;
return HttpResponseMessage.hasSuccessStatusCode(response) ? +response.headers.get("Content-Length")!.toString() : 0;
}
function readFile(path: string): string | undefined {
@@ -998,7 +996,7 @@ namespace Harness {
}
}
if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.getEnvironmentVariable("NODE_ENV"))) {
if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.getEnvironmentVariable!("NODE_ENV"))) {
Harness.IO.tryEnableSourceMapsForHost();
}
@@ -1030,7 +1028,7 @@ namespace Harness {
*/
export class WriterAggregator {
public lines: string[] = [];
public currentLine = <string>undefined;
public currentLine: string = undefined!;
public Write(str: string) {
// out of memory usage concerns avoid using + or += if we're going to do any manipulation of this string later
@@ -1040,17 +1038,17 @@ namespace Harness {
public WriteLine(str: string) {
// out of memory usage concerns avoid using + or += if we're going to do any manipulation of this string later
this.lines.push([(this.currentLine || ""), str].join(""));
this.currentLine = undefined;
this.currentLine = undefined!;
}
public Close() {
if (this.currentLine !== undefined) { this.lines.push(this.currentLine); }
this.currentLine = undefined;
this.currentLine = undefined!;
}
public reset() {
this.lines = [];
this.currentLine = undefined;
this.currentLine = undefined!;
}
}
@@ -1077,20 +1075,20 @@ namespace Harness {
// Cache of lib files from "built/local"
let libFileNameSourceFileMap: ts.Map<ts.SourceFile> | undefined;
export function getDefaultLibrarySourceFile(fileName = defaultLibFileName): ts.SourceFile {
export function getDefaultLibrarySourceFile(fileName = defaultLibFileName): ts.SourceFile | undefined {
if (!isDefaultLibraryFile(fileName)) {
return undefined;
}
if (!libFileNameSourceFileMap) {
libFileNameSourceFileMap = ts.createMapFromTemplate({
[defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest)
[defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts")!, /*languageVersion*/ ts.ScriptTarget.Latest)
});
}
let sourceFile = libFileNameSourceFileMap.get(fileName);
if (!sourceFile) {
libFileNameSourceFileMap.set(fileName, sourceFile = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest));
libFileNameSourceFileMap.set(fileName, sourceFile = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName)!, ts.ScriptTarget.Latest));
}
return sourceFile;
}
@@ -1206,10 +1204,10 @@ namespace Harness {
export function compileFiles(
inputFiles: TestFile[],
otherFiles: TestFile[],
harnessSettings: TestCaseParser.CompilerSettings,
compilerOptions: ts.CompilerOptions,
harnessSettings: TestCaseParser.CompilerSettings | undefined,
compilerOptions: ts.CompilerOptions | undefined,
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
currentDirectory: string): compiler.CompilationResult {
currentDirectory: string | undefined): compiler.CompilationResult {
const options: ts.CompilerOptions & HarnessOptions = compilerOptions ? ts.cloneCompilerOptions(compilerOptions) : { noResolve: false };
options.target = options.target || ts.ScriptTarget.ES3;
options.newLine = options.newLine || ts.NewLineKind.CarriageReturnLineFeed;
@@ -1225,7 +1223,7 @@ namespace Harness {
setCompilerOptionsFromHarnessSetting(harnessSettings, options);
}
if (options.rootDirs) {
options.rootDirs = ts.map(options.rootDirs, d => ts.getNormalizedAbsolutePath(d, currentDirectory));
options.rootDirs = ts.map(options.rootDirs, d => ts.getNormalizedAbsolutePath(d, currentDirectory!));
}
const useCaseSensitiveFileNames = options.useCaseSensitiveFileNames !== undefined ? options.useCaseSensitiveFileNames : true;
@@ -1253,7 +1251,7 @@ namespace Harness {
export interface DeclarationCompilationContext {
declInputFiles: TestFile[];
declOtherFiles: TestFile[];
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions;
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions | undefined;
options: ts.CompilerOptions;
currentDirectory: string;
}
@@ -1264,7 +1262,7 @@ namespace Harness {
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions,
options: ts.CompilerOptions,
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
currentDirectory: string): DeclarationCompilationContext | undefined {
currentDirectory: string | undefined): DeclarationCompilationContext | undefined {
if (options.declaration && result.diagnostics.length === 0) {
if (options.emitDeclarationOnly) {
@@ -1300,7 +1298,7 @@ namespace Harness {
}
function findResultCodeFile(fileName: string) {
const sourceFile = result.program.getSourceFile(fileName);
const sourceFile = result.program!.getSourceFile(fileName)!;
assert(sourceFile, "Program has no source file with name '" + fileName + "'");
// Is this file going to be emitted separately
let sourceFileName: string;
@@ -1308,7 +1306,7 @@ namespace Harness {
if (!outFile) {
if (options.outDir) {
let sourceFilePath = ts.getNormalizedAbsolutePath(sourceFile.fileName, result.vfs.cwd());
sourceFilePath = sourceFilePath.replace(result.program.getCommonSourceDirectory(), "");
sourceFilePath = sourceFilePath.replace(result.program!.getCommonSourceDirectory(), "");
sourceFileName = ts.combinePaths(options.outDir, sourceFilePath);
}
else {
@@ -1408,7 +1406,7 @@ namespace Harness {
// Filter down to the errors in the file
const fileErrors = diagnostics.filter((e): e is ts.DiagnosticWithLocation => {
const errFn = e.file;
return errFn && utils.removeTestPathPrefixes(errFn.fileName) === utils.removeTestPathPrefixes(inputFile.unitName);
return !!errFn && utils.removeTestPathPrefixes(errFn.fileName) === utils.removeTestPathPrefixes(inputFile.unitName);
});
@@ -1443,7 +1441,8 @@ namespace Harness {
}
// Emit this line from the original file
outputLines += (newLine() + " " + line);
fileErrors.forEach(err => {
fileErrors.forEach(errDiagnostic => {
const err = errDiagnostic as ts.TextSpan; // TODO: GH#18217
// Does any error start or continue on to this line? Emit squiggles
const end = ts.textSpanEnd(err);
if ((end >= thisLineStart) && ((err.start < nextLineStart) || (lineIndex === lines.length - 1))) {
@@ -1461,7 +1460,7 @@ namespace Harness {
// Just like above, we need to do a split on a string instead of on a regex
// because the JS engine does regexes wrong
outputErrorText(err);
outputErrorText(errDiagnostic);
markedErrorCount++;
}
}
@@ -1476,12 +1475,12 @@ namespace Harness {
}
const numLibraryDiagnostics = ts.countWhere(diagnostics, diagnostic => {
return diagnostic.file && (isDefaultLibraryFile(diagnostic.file.fileName) || isBuiltFile(diagnostic.file.fileName));
return !!diagnostic.file && (isDefaultLibraryFile(diagnostic.file.fileName) || isBuiltFile(diagnostic.file.fileName));
});
const numTest262HarnessDiagnostics = ts.countWhere(diagnostics, diagnostic => {
// Count an error generated from tests262-harness folder.This should only apply for test262
return diagnostic.file && diagnostic.file.fileName.indexOf("test262-harness") >= 0;
return !!diagnostic.file && diagnostic.file.fileName.indexOf("test262-harness") >= 0;
});
// Verify we didn't miss any errors in total
@@ -1489,7 +1488,7 @@ namespace Harness {
}
export function doErrorBaseline(baselinePath: string, inputFiles: ReadonlyArray<TestFile>, errors: ReadonlyArray<ts.Diagnostic>, pretty?: boolean) {
Baseline.runBaseline(baselinePath.replace(/\.tsx?$/, ".errors.txt"), (): string => {
Baseline.runBaseline(baselinePath.replace(/\.tsx?$/, ".errors.txt"), (): string | null => {
if (!errors || (errors.length === 0)) {
/* tslint:disable:no-null-keyword */
return null;
@@ -1519,7 +1518,7 @@ namespace Harness {
// Produce baselines. The first gives the types for all expressions.
// The second gives symbols for all identifiers.
let typesError: Error, symbolsError: Error;
let typesError: Error | undefined, symbolsError: Error | undefined;
try {
checkBaseLines(/*isSymbolBaseLine*/ false);
}
@@ -1567,7 +1566,7 @@ namespace Harness {
}
}
function generateBaseLine(isSymbolBaseline: boolean, skipBaseline?: boolean): string {
function generateBaseLine(isSymbolBaseline: boolean, skipBaseline?: boolean): string | null {
let result = "";
const gen = iterateBaseLine(isSymbolBaseline, skipBaseline);
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
@@ -1754,7 +1753,7 @@ namespace Harness {
resultName = sanitizeTestFilePath(resultName);
if (dupeCase.has(resultName)) {
// A different baseline filename should be manufactured if the names differ only in case, for windows compat
const count = 1 + dupeCase.get(resultName);
const count = 1 + dupeCase.get(resultName)!;
dupeCase.set(resultName, count);
resultName = `${resultName}.dupe${count}`;
}
@@ -1862,7 +1861,7 @@ namespace Harness {
export function extractCompilerSettings(content: string): CompilerSettings {
const opts: CompilerSettings = {};
let match: RegExpExecArray;
let match: RegExpExecArray | null;
/* tslint:disable:no-null-keyword */
while ((match = optionRegex.exec(content)) !== null) {
/* tslint:enable:no-null-keyword */
@@ -1875,8 +1874,8 @@ namespace Harness {
export interface TestCaseContent {
settings: CompilerSettings;
testUnitData: TestUnitData[];
tsConfig: ts.ParsedCommandLine;
tsConfigFileUnitData: TestUnitData;
tsConfig: ts.ParsedCommandLine | undefined;
tsConfigFileUnitData: TestUnitData | undefined;
}
/** Given a test file containing // @FileName directives, return an array of named units of code to be added to an existing compiler instance */
@@ -1887,7 +1886,7 @@ namespace Harness {
const lines = Utils.splitContentByNewlines(code);
// Stuff related to the subfile we're parsing
let currentFileContent: string;
let currentFileContent: string | undefined;
let currentFileOptions: any = {};
let currentFileName: any;
let refs: string[] = [];
@@ -1907,7 +1906,7 @@ namespace Harness {
if (currentFileName) {
// Store result file
const newTestFile = {
content: currentFileContent,
content: currentFileContent!, // TODO: GH#18217
name: currentFileName,
fileOptions: currentFileOptions,
originalFilePath: fileName,
@@ -1962,8 +1961,8 @@ namespace Harness {
};
// check if project has tsconfig.json in the list of files
let tsConfig: ts.ParsedCommandLine;
let tsConfigFileUnitData: TestUnitData;
let tsConfig: ts.ParsedCommandLine | undefined;
let tsConfigFileUnitData: TestUnitData | undefined;
for (let i = 0; i < testUnitData.length; i++) {
const data = testUnitData[i];
if (getConfigNameFromFileName(data.name)) {
@@ -2024,7 +2023,7 @@ namespace Harness {
}
const fileCache: { [idx: string]: boolean } = {};
function generateActual(generateContent: () => string): string {
function generateActual(generateContent: () => string | null): string | null {
const actual = generateContent();
@@ -2035,12 +2034,12 @@ namespace Harness {
return actual;
}
function compareToBaseline(actual: string, relativeFileName: string, opts: BaselineOptions) {
function compareToBaseline(actual: string | null, relativeFileName: string, opts: BaselineOptions | undefined) {
// actual is now either undefined (the generator had an error), null (no file requested),
// or some real output of the function
if (actual === undefined) {
// Nothing to do
return;
return undefined!; // TODO: GH#18217
}
const refFileName = referencePath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder);
@@ -2053,7 +2052,7 @@ namespace Harness {
let expected = "<no content>";
if (IO.fileExists(refFileName)) {
expected = IO.readFile(refFileName);
expected = IO.readFile(refFileName)!; // TODO: GH#18217
}
return { expected, actual };
@@ -2069,7 +2068,7 @@ namespace Harness {
return;
}
const parentDirectory = IO.directoryName(dirName);
const parentDirectory = IO.directoryName(dirName)!; // TODO: GH#18217
if (parentDirectory !== "" && parentDirectory !== dirName) {
createDirectoryStructure(parentDirectory);
}
@@ -2078,7 +2077,7 @@ namespace Harness {
}
// Create folders if needed
createDirectoryStructure(IO.directoryName(actualFileName));
createDirectoryStructure(IO.directoryName(actualFileName)!); // TODO: GH#18217
// Delete the actual file in case it fails
if (IO.fileExists(actualFileName)) {
@@ -2097,14 +2096,14 @@ namespace Harness {
}
}
export function runBaseline(relativeFileName: string, generateContent: () => string, opts?: BaselineOptions): void {
export function runBaseline(relativeFileName: string, generateContent: () => string | null, opts?: BaselineOptions): void {
const actualFileName = localPath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder);
const actual = generateActual(generateContent);
const comparison = compareToBaseline(actual, relativeFileName, opts);
writeComparison(comparison.expected, comparison.actual, relativeFileName, actualFileName);
}
export function runMultifileBaseline(relativeFileBase: string, extension: string, generateContent: () => IterableIterator<[string, string, number]> | IterableIterator<[string, string]>, opts?: BaselineOptions, referencedExtensions?: string[]): void {
export function runMultifileBaseline(relativeFileBase: string, extension: string, generateContent: () => IterableIterator<[string, string, number]> | IterableIterator<[string, string]> | null, opts?: BaselineOptions, referencedExtensions?: string[]): void {
const gen = generateContent();
const writtenFiles = ts.createMap<true>();
const errors: Error[] = [];
@@ -2175,7 +2174,7 @@ namespace Harness {
export function getDefaultLibraryFile(filePath: string, io: IO): Compiler.TestFile {
const libFile = userSpecifiedRoot + libFolder + ts.getBaseFileName(ts.normalizeSlashes(filePath));
return { unitName: libFile, content: io.readFile(libFile) };
return { unitName: libFile, content: io.readFile(libFile)! };
}
export function getConfigNameFromFileName(filename: string): "tsconfig.json" | "jsconfig.json" | undefined {
+16 -17
View File
@@ -7,7 +7,7 @@ namespace Harness.LanguageService {
export class ScriptInfo {
public version = 1;
public editRanges: { length: number; textChangeRange: ts.TextChangeRange; }[] = [];
private lineMap: number[] = undefined;
private lineMap: number[] | undefined;
constructor(public fileName: string, public content: string, public isRootFile: boolean) {
this.setContent(content);
@@ -95,7 +95,7 @@ namespace Harness.LanguageService {
return this.scriptSnapshot.getLength();
}
public getChangeRange(oldScript: ts.ScriptSnapshotShim): string {
public getChangeRange(oldScript: ts.ScriptSnapshotShim): string | undefined {
const range = this.scriptSnapshot.getChangeRange((oldScript as ScriptSnapshotProxy).scriptSnapshot);
return range && JSON.stringify(range);
}
@@ -146,7 +146,7 @@ namespace Harness.LanguageService {
return fileNames;
}
public getScriptInfo(fileName: string): ScriptInfo {
public getScriptInfo(fileName: string): ScriptInfo | undefined {
return this.scriptInfos.get(vpath.resolve(this.vfs.cwd(), fileName));
}
@@ -175,9 +175,8 @@ namespace Harness.LanguageService {
* @param col 0 based index
*/
public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter {
const script: ScriptInfo = this.getScriptInfo(fileName);
const script: ScriptInfo = this.getScriptInfo(fileName)!;
assert.isOk(script);
return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position);
}
}
@@ -185,7 +184,7 @@ namespace Harness.LanguageService {
/// Native adapter
class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost, LanguageServiceAdapterHost {
isKnownTypesPackageName(name: string): boolean {
return this.typesRegistry && this.typesRegistry.has(name);
return !!this.typesRegistry && this.typesRegistry.has(name);
}
installPackage = ts.notImplemented;
@@ -206,7 +205,7 @@ namespace Harness.LanguageService {
return this.getFilenames().filter(ts.isAnySupportedFileExtension);
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
const script = this.getScriptInfo(fileName);
return script ? new ScriptSnapshot(script) : undefined;
}
@@ -215,7 +214,7 @@ namespace Harness.LanguageService {
getScriptVersion(fileName: string): string {
const script = this.getScriptInfo(fileName);
return script ? script.version.toString() : undefined;
return script ? script.version.toString() : undefined!; // TODO: GH#18217
}
directoryExists(dirName: string): boolean {
@@ -280,7 +279,7 @@ namespace Harness.LanguageService {
}
};
this.getModuleResolutionsForFile = (fileName) => {
const scriptInfo = this.getScriptInfo(fileName);
const scriptInfo = this.getScriptInfo(fileName)!;
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports: ts.MapLike<string> = {};
for (const module of preprocessInfo.importedFiles) {
@@ -299,8 +298,8 @@ namespace Harness.LanguageService {
const settings = this.nativeHost.getCompilationSettings();
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost);
if (resolutionInfo.resolvedTypeReferenceDirective.resolvedFileName) {
resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective;
if (resolutionInfo.resolvedTypeReferenceDirective!.resolvedFileName) {
resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective!;
}
}
return JSON.stringify(resolutions);
@@ -313,7 +312,7 @@ namespace Harness.LanguageService {
}
getFilenames(): string[] { return this.nativeHost.getFilenames(); }
getScriptInfo(fileName: string): ScriptInfo { return this.nativeHost.getScriptInfo(fileName); }
getScriptInfo(fileName: string): ScriptInfo | undefined { return this.nativeHost.getScriptInfo(fileName); }
addScript(fileName: string, content: string, isRootFile: boolean): void { this.nativeHost.addScript(fileName, content, isRootFile); }
editScript(fileName: string, start: number, end: number, newText: string): void { this.nativeHost.editScript(fileName, start, end, newText); }
positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToLineAndCharacter(fileName, position); }
@@ -325,7 +324,7 @@ namespace Harness.LanguageService {
getDefaultLibFileName(): string { return this.nativeHost.getDefaultLibFileName(); }
getScriptFileNames(): string { return JSON.stringify(this.nativeHost.getScriptFileNames()); }
getScriptSnapshot(fileName: string): ts.ScriptSnapshotShim {
const nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName);
const nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName)!; // TODO: GH#18217
return nativeScriptSnapshot && new ScriptSnapshotProxy(nativeScriptSnapshot);
}
getScriptKind(): ts.ScriptKind { return this.nativeHost.getScriptKind(); }
@@ -504,7 +503,7 @@ namespace Harness.LanguageService {
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: ts.FormatCodeOptions): ts.TextChange[] {
return unwrapJSONCallResult(this.shim.getFormattingEditsAfterKeystroke(fileName, position, key, JSON.stringify(options)));
}
getDocCommentTemplateAtPosition(fileName: string, position: number): ts.TextInsertion | undefined {
getDocCommentTemplateAtPosition(fileName: string, position: number): ts.TextInsertion {
return unwrapJSONCallResult(this.shim.getDocCommentTemplateAtPosition(fileName, position));
}
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
@@ -608,7 +607,7 @@ namespace Harness.LanguageService {
class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost {
private client: ts.server.SessionClient;
constructor(cancellationToken: ts.HostCancellationToken, settings: ts.CompilerOptions) {
constructor(cancellationToken: ts.HostCancellationToken | undefined, settings: ts.CompilerOptions | undefined) {
super(cancellationToken, settings);
}
@@ -715,7 +714,7 @@ namespace Harness.LanguageService {
return true;
}
getLogFileName(): string {
getLogFileName(): string | undefined {
return undefined;
}
@@ -850,7 +849,7 @@ namespace Harness.LanguageService {
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
typingsInstaller: undefined!, // TODO: GH#18217
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: serverHost,
+38 -35
View File
@@ -68,10 +68,10 @@ interface IoLog {
}[];
directoriesRead: {
path: string,
extensions: ReadonlyArray<string>,
exclude: ReadonlyArray<string>,
include: ReadonlyArray<string>,
depth: number,
extensions: ReadonlyArray<string> | undefined,
exclude: ReadonlyArray<string> | undefined,
include: ReadonlyArray<string> | undefined,
depth: number | undefined,
result: ReadonlyArray<string>,
}[];
useCaseSensitiveFileNames?: boolean;
@@ -87,8 +87,8 @@ interface PlaybackControl {
}
namespace Playback {
let recordLog: IoLog;
let replayLog: IoLog;
let recordLog: IoLog | undefined;
let replayLog: IoLog | undefined;
let replayFilesRead: ts.Map<IoLogFile> | undefined;
let recordLogFileNameBase = "";
@@ -104,7 +104,7 @@ namespace Playback {
return lookup[s] = func(s);
});
run.reset = () => {
lookup = undefined;
lookup = undefined!; // TODO: GH#18217
};
return run;
@@ -148,11 +148,12 @@ namespace Playback {
}
}
for (const file of log.filesRead) {
if (file.result.contentsPath) {
const result = file.result!; // TODO: GH#18217
if (result.contentsPath) {
// `readFile` strips away a BOM (and actually reinerprets the file contents according to the correct encoding)
// - but this has the unfortunate sideeffect of removing the BOM from any outputs based on the file, so we readd it here.
file.result.contents = (file.result.bom || "") + host.readFile(ts.combinePaths(baseName, file.result.contentsPath));
delete file.result.contentsPath;
result.contents = (result.bom || "") + host.readFile(ts.combinePaths(baseName, result.contentsPath));
delete result.contentsPath;
}
}
return log;
@@ -188,21 +189,22 @@ namespace Playback {
}
if (log.filesRead) {
for (const file of log.filesRead) {
const { contents } = file.result;
const result = file.result!; // TODO: GH#18217
const { contents } = result;
if (contents !== undefined) {
file.result.contentsPath = ts.combinePaths("read", sanitizeTestFilePath(file.path));
writeFile(ts.combinePaths(baseTestName, file.result.contentsPath), contents);
result.contentsPath = ts.combinePaths("read", sanitizeTestFilePath(file.path));
writeFile(ts.combinePaths(baseTestName, result.contentsPath), contents);
const len = contents.length;
if (len >= 2 && contents.charCodeAt(0) === 0xfeff) {
file.result.bom = "\ufeff";
result.bom = "\ufeff";
}
if (len >= 2 && contents.charCodeAt(0) === 0xfffe) {
file.result.bom = "\ufffe";
result.bom = "\ufffe";
}
if (len >= 3 && contents.charCodeAt(0) === 0xefbb && contents.charCodeAt(1) === 0xbf) {
file.result.bom = "\uefbb\xbf";
result.bom = "\uefbb\xbf";
}
delete file.result.contents;
delete result.contents;
}
}
}
@@ -222,7 +224,7 @@ namespace Playback {
wrapper.startReplayFromData = log => {
replayLog = log;
// Remove non-found files from the log (shouldn't really need them, but we still record them for diagnostic purposes)
replayLog.filesRead = replayLog.filesRead.filter(f => f.result.contents !== undefined);
replayLog.filesRead = replayLog.filesRead.filter(f => f.result!.contents !== undefined);
replayFilesRead = ts.createMap();
for (const file of replayLog.filesRead) {
replayFilesRead.set(ts.normalizeSlashes(file.path).toLowerCase(), file);
@@ -244,7 +246,7 @@ namespace Playback {
};
wrapper.startReplayFromFile = logFn => {
wrapper.startReplayFromString(underlying.readFile(logFn));
wrapper.startReplayFromString(underlying.readFile(logFn)!);
};
wrapper.endRecord = () => {
if (recordLog !== undefined) {
@@ -267,24 +269,25 @@ namespace Playback {
}
const files = [];
for (const file of newLog.filesRead) {
if (file.result.contentsPath &&
Harness.isDefaultLibraryFile(file.result.contentsPath) &&
/\.[tj]s$/.test(file.result.contentsPath)) {
files.push(file.result.contentsPath);
const result = file.result!;
if (result.contentsPath &&
Harness.isDefaultLibraryFile(result.contentsPath) &&
/\.[tj]s$/.test(result.contentsPath)) {
files.push(result.contentsPath);
}
}
return { compilerOptions: ts.parseCommandLine(newLog.arguments).options, files };
}
wrapper.fileExists = recordReplay(wrapper.fileExists, underlying)(
path => callAndRecord(underlying.fileExists(path), recordLog.fileExists, { path }),
path => callAndRecord(underlying.fileExists(path), recordLog!.fileExists, { path }),
memoize(path => {
// If we read from the file, it must exist
if (findFileByPath(path, /*throwFileNotFoundError*/ false)) {
return true;
}
else {
return findResultByFields(replayLog.fileExists, { path }, /*defaultValue*/ false);
return findResultByFields(replayLog!.fileExists, { path }, /*defaultValue*/ false)!;
}
})
);
@@ -314,22 +317,22 @@ namespace Playback {
};
wrapper.resolvePath = recordReplay(wrapper.resolvePath, underlying)(
path => callAndRecord(underlying.resolvePath(path), recordLog.pathsResolved, { path }),
memoize(path => findResultByFields(replayLog.pathsResolved, { path }, !ts.isRootedDiskPath(ts.normalizeSlashes(path)) && replayLog.currentDirectory ? replayLog.currentDirectory + "/" + path : ts.normalizeSlashes(path))));
path => callAndRecord(underlying.resolvePath(path), recordLog!.pathsResolved, { path }),
memoize(path => findResultByFields(replayLog!.pathsResolved, { path }, !ts.isRootedDiskPath(ts.normalizeSlashes(path)) && replayLog!.currentDirectory ? replayLog!.currentDirectory + "/" + path : ts.normalizeSlashes(path))));
wrapper.readFile = recordReplay(wrapper.readFile, underlying)(
(path: string) => {
const result = underlying.readFile(path);
const logEntry = { path, codepage: 0, result: { contents: result, codepage: 0 } };
recordLog.filesRead.push(logEntry);
recordLog!.filesRead.push(logEntry);
return result;
},
memoize(path => findFileByPath(path, /*throwFileNotFoundError*/ true).contents));
memoize(path => findFileByPath(path, /*throwFileNotFoundError*/ true)!.contents));
wrapper.readDirectory = recordReplay(wrapper.readDirectory, underlying)(
(path, extensions, exclude, include, depth) => {
const result = (<ts.System>underlying).readDirectory(path, extensions, exclude, include, depth);
recordLog.directoriesRead.push({ path, extensions, exclude, include, depth, result });
recordLog!.directoriesRead.push({ path, extensions, exclude, include, depth, result });
return result;
},
path => {
@@ -338,7 +341,7 @@ namespace Playback {
// different entry).
// TODO (yuisu): We can certainly remove these once we recapture the RWC using new API
const normalizedPath = ts.normalizePath(path).toLowerCase();
return ts.flatMap(replayLog.directoriesRead, directory => {
return ts.flatMap(replayLog!.directoriesRead, directory => {
if (ts.normalizeSlashes(directory.path).toLowerCase() === normalizedPath) {
return directory.result;
}
@@ -346,7 +349,7 @@ namespace Playback {
});
wrapper.writeFile = recordReplay(wrapper.writeFile, underlying)(
(path: string, contents: string) => callAndRecord(underlying.writeFile(path, contents), recordLog.filesWritten, { path, contents, bom: false }),
(path: string, contents: string) => callAndRecord(underlying.writeFile(path, contents), recordLog!.filesWritten, { path, contents, bom: false }),
() => noOpReplay("writeFile"));
wrapper.exit = (exitCode) => {
@@ -390,7 +393,7 @@ namespace Playback {
return underlyingResult;
}
function findResultByFields<T>(logArray: { result?: T }[], expectedFields: {}, defaultValue?: T): T {
function findResultByFields<T>(logArray: { result?: T }[], expectedFields: {}, defaultValue?: T): T | undefined {
const predicate = (entry: { result?: T }) => {
return Object.getOwnPropertyNames(expectedFields).every((name) => (<any>entry)[name] === (<any>expectedFields)[name]);
};
@@ -406,10 +409,10 @@ namespace Playback {
return results[0].result;
}
function findFileByPath(expectedPath: string, throwFileNotFoundError: boolean): FileInformation {
function findFileByPath(expectedPath: string, throwFileNotFoundError: boolean): FileInformation | undefined {
const normalizedName = ts.normalizePath(expectedPath).toLowerCase();
// Try to find the result through normal fileName
const result = replayFilesRead.get(normalizedName);
const result = replayFilesRead!.get(normalizedName);
if (result) {
return result.result;
}
+14 -12
View File
@@ -1,3 +1,5 @@
// tslint:disable no-unnecessary-type-assertion (TODO: tslint can't find node types)
if (typeof describe === "undefined") {
(global as any).describe = undefined; // If launched without mocha for parallel mode, we still need a global describe visible to satisfy the parsing of the unit tests
(global as any).it = undefined;
@@ -32,7 +34,7 @@ namespace Harness.Parallel.Host {
function perfdataFileName(target?: string) {
return `${perfdataFileNameFragment}${target ? `.${target}` : ""}.json`;
}
function readSavedPerfData(target?: string): {[testHash: string]: number} {
function readSavedPerfData(target?: string): {[testHash: string]: number} | undefined {
const perfDataContents = IO.readFile(perfdataFileName(target));
if (perfDataContents) {
return JSON.parse(perfDataContents);
@@ -73,7 +75,7 @@ namespace Harness.Parallel.Host {
setTimeout(() => startDelayed(perfData, totalCost), 0); // Do real startup on next tick, so all unit tests have been collected
}
function startDelayed(perfData: {[testHash: string]: number}, totalCost: number) {
function startDelayed(perfData: {[testHash: string]: number} | undefined, totalCost: number) {
initializeProgressBarsDependencies();
console.log(`Discovered ${tasks.length} unittest suites` + (newTasks.length ? ` and ${newTasks.length} new suites.` : "."));
console.log("Discovering runner-based tests...");
@@ -227,16 +229,16 @@ namespace Harness.Parallel.Host {
return;
}
// Send tasks in blocks if the tasks are small
const taskList = [tasks.pop()];
const taskList = [tasks.pop()!];
while (tasks.length && taskList.reduce((p, c) => p + c.size, 0) < chunkSize) {
taskList.push(tasks.pop());
taskList.push(tasks.pop()!);
}
child.currentTasks = taskList;
if (taskList.length === 1) {
child.send({ type: "test", payload: taskList[0] });
child.send({ type: "test", payload: taskList[0] } as ParallelHostMessage); // TODO: GH#18217
}
else {
child.send({ type: "batch", payload: taskList });
child.send({ type: "batch", payload: taskList } as ParallelHostMessage); // TODO: GH#18217
}
}
}
@@ -268,7 +270,7 @@ namespace Harness.Parallel.Host {
doneBatching[i] = true;
continue;
}
const task = tasks.pop();
const task = tasks.pop()!;
batches[i].push(task);
scheduledTotal += task.size;
}
@@ -293,7 +295,7 @@ namespace Harness.Parallel.Host {
worker.send({ type: "batch", payload });
}
else { // Out of batches, send off just one test
const payload = tasks.pop();
const payload = tasks.pop()!;
ts.Debug.assert(!!payload); // The reserve kept above should ensure there is always an initial task available, even in suboptimal scenarios
worker.currentTasks = [payload];
worker.send({ type: "test", payload });
@@ -302,7 +304,7 @@ namespace Harness.Parallel.Host {
}
else {
for (let i = 0; i < workerCount; i++) {
const task = tasks.pop();
const task = tasks.pop()!;
workers[i].currentTasks = [task];
workers[i].send({ type: "test", payload: task });
}
@@ -519,7 +521,7 @@ namespace Harness.Parallel.Host {
this._enabled = false;
}
}
update(index: number, percentComplete: number, color: string, title: string, titleColor?: string) {
update(index: number, percentComplete: number, color: string, title: string | undefined, titleColor?: string) {
percentComplete = minMax(percentComplete, 0, 1);
const progressBar = this._progressBars[index] || (this._progressBars[index] = { });
@@ -555,7 +557,7 @@ namespace Harness.Parallel.Host {
}
cursor.hide();
readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount);
readline.moveCursor(process.stdout, -process.stdout.columns!, -this._lineCount);
let lineCount = 0;
const numProgressBars = this._progressBars.length;
for (let i = 0; i < numProgressBars; i++) {
@@ -564,7 +566,7 @@ namespace Harness.Parallel.Host {
process.stdout.write(this._progressBars[i].text + os.EOL);
}
else {
readline.moveCursor(process.stdout, -process.stdout.columns, +1);
readline.moveCursor(process.stdout, -process.stdout.columns!, +1);
}
lineCount++;
+17 -15
View File
@@ -1,3 +1,5 @@
// tslint:disable no-unnecessary-type-assertion (TODO: tslint can't find node types)
namespace Harness.Parallel.Worker {
let errors: ErrorInfo[] = [];
let passes: TestInfo[] = [];
@@ -35,7 +37,7 @@ namespace Harness.Parallel.Worker {
if (!testList) {
throw new Error("Tests must occur within a describe block");
}
testList.push({ name, callback, kind: "test" });
testList.push({ name, callback: callback!, kind: "test" });
}) as Mocha.ITestDefinition;
(global as any).it.skip = ts.noop;
}
@@ -43,18 +45,18 @@ namespace Harness.Parallel.Worker {
function setTimeoutAndExecute(timeout: number | undefined, f: () => void) {
if (timeout !== undefined) {
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: timeout } };
process.send(timeoutMsg);
process.send!(timeoutMsg);
}
f();
if (timeout !== undefined) {
// Reset timeout
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: "reset" } };
process.send(timeoutMsg);
process.send!(timeoutMsg);
}
}
function executeSuiteCallback(name: string, callback: MochaCallback) {
let timeout: number;
let timeout: number | undefined;
const fakeContext: Mocha.ISuiteCallbackContext = {
retries() { return this; },
slow() { return this; },
@@ -64,9 +66,9 @@ namespace Harness.Parallel.Worker {
},
};
namestack.push(name);
let beforeFunc: Callable;
let beforeFunc: Callable | undefined;
(before as any) = (cb: Callable) => beforeFunc = cb;
let afterFunc: Callable;
let afterFunc: Callable | undefined;
(after as any) = (cb: Callable) => afterFunc = cb;
const savedBeforeEach = beforeEachFunc;
(beforeEach as any) = (cb: Callable) => beforeEachFunc = cb;
@@ -127,13 +129,13 @@ namespace Harness.Parallel.Worker {
}
function executeTestCallback(name: string, callback: MochaCallback) {
let timeout: number;
let timeout: number | undefined;
const fakeContext: Mocha.ITestCallbackContext = {
skip() { return this; },
timeout(n: number) {
timeout = n;
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: timeout } };
process.send(timeoutMsg);
process.send!(timeoutMsg);
return this;
},
retries() { return this; },
@@ -164,7 +166,7 @@ namespace Harness.Parallel.Worker {
namestack.pop();
if (timeout !== undefined) {
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: "reset" } };
process.send(timeoutMsg);
process.send!(timeoutMsg);
}
}
passing++;
@@ -195,7 +197,7 @@ namespace Harness.Parallel.Worker {
namestack.pop();
if (timeout !== undefined) {
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: "reset" } };
process.send(timeoutMsg);
process.send!(timeoutMsg);
}
}
if (!completed) {
@@ -219,7 +221,7 @@ namespace Harness.Parallel.Worker {
console.error(data);
}
const message: ParallelResultMessage = { type: "result", payload: handleTest(runner, file) };
process.send(message);
process.send!(message);
break;
case "close":
process.exit(0);
@@ -239,16 +241,16 @@ namespace Harness.Parallel.Worker {
else {
message = { type: "progress", payload };
}
process.send(message);
process.send!(message);
}
break;
}
}
});
process.on("uncaughtException", error => {
const message: ParallelErrorMessage = { type: "error", payload: { error: error.message, stack: error.stack, name: [...namestack] } };
const message: ParallelErrorMessage = { type: "error", payload: { error: error.message, stack: error.stack!, name: [...namestack] } };
try {
process.send(message);
process.send!(message);
}
catch (e) {
console.error(error);
@@ -273,7 +275,7 @@ namespace Harness.Parallel.Worker {
if (!runners.has(runner)) {
runners.set(runner, createRunner(runner));
}
const instance = runners.get(runner);
const instance = runners.get(runner)!;
instance.tests = [file];
return { ...resetShimHarnessAndExecute(instance), runner, file };
}
+17 -17
View File
@@ -134,7 +134,7 @@ namespace project {
this.compilerOptions = createCompilerOptions(testCase, moduleKind);
this.sys = new fakes.System(vfs);
let configFileName: string;
let configFileName: string | undefined;
let inputFiles = testCase.inputFiles;
if (this.compilerOptions.project) {
// Parse project
@@ -145,7 +145,7 @@ namespace project {
configFileName = ts.findConfigFile("", path => this.sys.fileExists(path));
}
let errors: ts.Diagnostic[];
let errors: ts.Diagnostic[] | undefined;
const configFileSourceFiles: ts.SourceFile[] = [];
if (configFileName) {
const result = ts.readJsonConfigFile(configFileName, path => this.sys.readFile(path));
@@ -178,7 +178,7 @@ namespace project {
public static getConfigurations(testCaseFileName: string): ProjectTestConfiguration[] {
let testCase: ProjectRunnerTestCase & ts.CompilerOptions;
let testFileText: string;
let testFileText: string | undefined;
try {
testFileText = Harness.IO.readFile(testCaseFileName);
}
@@ -187,10 +187,10 @@ namespace project {
}
try {
testCase = <ProjectRunnerTestCase & ts.CompilerOptions>JSON.parse(testFileText);
testCase = <ProjectRunnerTestCase & ts.CompilerOptions>JSON.parse(testFileText!);
}
catch (e) {
assert(false, "Testcase: " + testCaseFileName + " does not contain valid json format: " + e.message);
throw assert(false, "Testcase: " + testCaseFileName + " does not contain valid json format: " + e.message);
}
const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false);
@@ -209,12 +209,12 @@ namespace project {
const cwd = this.vfs.cwd();
const ignoreCase = this.vfs.ignoreCase;
const resolutionInfo: ProjectRunnerTestCaseResolutionInfo & ts.CompilerOptions = JSON.parse(JSON.stringify(this.testCase));
resolutionInfo.resolvedInputFiles = this.compilerResult.program.getSourceFiles()
resolutionInfo.resolvedInputFiles = this.compilerResult.program!.getSourceFiles()
.map(({ fileName: input }) => vpath.beneath(vfs.builtFolder, input, this.vfs.ignoreCase) || vpath.beneath(vfs.testLibFolder, input, this.vfs.ignoreCase) ? utils.removeTestPathPrefixes(input) :
vpath.isAbsolute(input) ? vpath.relative(cwd, input, ignoreCase) :
input);
resolutionInfo.emittedFiles = this.compilerResult.outputFiles
resolutionInfo.emittedFiles = this.compilerResult.outputFiles!
.map(output => output.meta.get("fileName") || output.file)
.map(output => utils.removeTestPathPrefixes(vpath.isAbsolute(output) ? vpath.relative(cwd, output, ignoreCase) : output));
@@ -234,7 +234,7 @@ namespace project {
if (this.testCase.baselineCheck) {
const errs: Error[] = [];
let nonSubfolderDiskFiles = 0;
for (const output of this.compilerResult.outputFiles) {
for (const output of this.compilerResult.outputFiles!) {
try {
// convert file name to rooted name
// if filename is not rooted - concat it with project root and then expand project root relative to current directory
@@ -252,7 +252,7 @@ namespace project {
}
const content = utils.removeTestPathPrefixes(output.text, /*retainTrailingDirectorySeparator*/ true);
Harness.Baseline.runBaseline(this.getBaselineFolder(this.compilerResult.moduleKind) + diskRelativeName, () => content);
Harness.Baseline.runBaseline(this.getBaselineFolder(this.compilerResult.moduleKind) + diskRelativeName, () => content as string | null); // TODO: GH#18217
}
catch (e) {
errs.push(e);
@@ -292,7 +292,7 @@ namespace project {
}
private cleanProjectUrl(url: string) {
let diskProjectPath = ts.normalizeSlashes(Harness.IO.resolvePath(this.testCase.projectRoot));
let diskProjectPath = ts.normalizeSlashes(Harness.IO.resolvePath(this.testCase.projectRoot)!);
let projectRootUrl = "file:///" + diskProjectPath;
const normalizedProjectRoot = ts.normalizeSlashes(this.testCase.projectRoot);
diskProjectPath = diskProjectPath.substr(0, diskProjectPath.lastIndexOf(normalizedProjectRoot));
@@ -362,10 +362,10 @@ namespace project {
rootFiles.unshift(sourceFile.fileName);
}
else if (!(compilerOptions.outFile || compilerOptions.out)) {
let emitOutputFilePathWithoutExtension: string;
let emitOutputFilePathWithoutExtension: string | undefined;
if (compilerOptions.outDir) {
let sourceFilePath = ts.getNormalizedAbsolutePath(sourceFile.fileName, compilerResult.program.getCurrentDirectory());
sourceFilePath = sourceFilePath.replace(compilerResult.program.getCommonSourceDirectory(), "");
let sourceFilePath = ts.getNormalizedAbsolutePath(sourceFile.fileName, compilerResult.program!.getCurrentDirectory());
sourceFilePath = sourceFilePath.replace(compilerResult.program!.getCommonSourceDirectory(), "");
emitOutputFilePathWithoutExtension = ts.removeFileExtension(ts.combinePaths(compilerOptions.outDir, sourceFilePath));
}
else {
@@ -380,8 +380,8 @@ namespace project {
}
}
else {
const outputDtsFileName = ts.removeFileExtension(compilerOptions.outFile || compilerOptions.out) + ts.Extension.Dts;
const outputDtsFile = findOutputDtsFile(outputDtsFileName);
const outputDtsFileName = ts.removeFileExtension(compilerOptions.outFile || compilerOptions.out!) + ts.Extension.Dts;
const outputDtsFile = findOutputDtsFile(outputDtsFileName)!;
if (!ts.contains(allInputFiles, outputDtsFile)) {
allInputFiles.unshift(outputDtsFile);
rootFiles.unshift(outputDtsFile.meta.get("fileName") || outputDtsFile.file);
@@ -395,8 +395,8 @@ namespace project {
});
// Dont allow config files since we are compiling existing source options
const compilerHost = new ProjectCompilerHost(_vfs, compilerResult.compilerOptions, this.testCaseJustName, this.testCase, compilerResult.moduleKind);
return this.compileProjectFiles(compilerResult.moduleKind, compilerResult.configFileSourceFiles, () => rootFiles, compilerHost, compilerResult.compilerOptions);
const compilerHost = new ProjectCompilerHost(_vfs, compilerResult.compilerOptions!, this.testCaseJustName, this.testCase, compilerResult.moduleKind);
return this.compileProjectFiles(compilerResult.moduleKind, compilerResult.configFileSourceFiles, () => rootFiles, compilerHost, compilerResult.compilerOptions!);
function findOutputDtsFile(fileName: string) {
return ts.forEach(compilerResult.outputFiles, outputFile => outputFile.meta.get("fileName") === fileName ? outputFile : undefined);
+8 -7
View File
@@ -65,7 +65,7 @@ function createRunner(kind: TestRunnerKind): RunnerBase {
case "dt":
return new DefinitelyTypedRunner();
}
ts.Debug.fail(`Unknown runner kind ${kind}`);
return ts.Debug.fail(`Unknown runner kind ${kind}`);
}
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
@@ -76,10 +76,10 @@ const testconfigFileName = "test.config";
const customConfig = tryGetConfig(Harness.IO.args());
let testConfigContent =
customConfig && Harness.IO.fileExists(customConfig)
? Harness.IO.readFile(customConfig)
? Harness.IO.readFile(customConfig)!
: Harness.IO.fileExists(mytestconfigFileName)
? Harness.IO.readFile(mytestconfigFileName)
: Harness.IO.fileExists(testconfigFileName) ? Harness.IO.readFile(testconfigFileName) : "";
? Harness.IO.readFile(mytestconfigFileName)!
: Harness.IO.fileExists(testconfigFileName) ? Harness.IO.readFile(testconfigFileName)! : "";
let taskConfigsFolder: string;
let workerCount: number;
@@ -131,9 +131,9 @@ function handleTestConfig() {
(<any>Error).stackTraceLimit = Infinity;
stackTraceLimit = testConfig.stackTraceLimit;
}
else if ((+testConfig.stackTraceLimit | 0) > 0) {
(<any>Error).stackTraceLimit = +testConfig.stackTraceLimit | 0;
stackTraceLimit = +testConfig.stackTraceLimit | 0;
else if ((+testConfig.stackTraceLimit! | 0) > 0) {
(<any>Error).stackTraceLimit = +testConfig.stackTraceLimit! | 0;
stackTraceLimit = +testConfig.stackTraceLimit! | 0;
}
if (testConfig.listenForWork) {
return true;
@@ -222,6 +222,7 @@ function handleTestConfig() {
if (runUnitTests === undefined) {
runUnitTests = runners.length !== 1; // Don't run unit tests when running only one runner if unit tests were not explicitly asked for
}
return false;
}
function beginTests() {
+15 -15
View File
@@ -41,22 +41,22 @@ namespace RWC {
inputFiles = [];
otherFiles = [];
tsconfigFiles = [];
compilerResult = undefined;
compilerOptions = undefined;
currentDirectory = undefined;
compilerResult = undefined!;
compilerOptions = undefined!;
currentDirectory = undefined!;
// useCustomLibraryFile is a flag specified in the json object to indicate whether to use built/local/lib.d.ts
// or to use lib.d.ts inside the json object. If the flag is true, use the lib.d.ts inside json file
// otherwise use the lib.d.ts from built/local
useCustomLibraryFile = undefined;
useCustomLibraryFile = undefined!;
});
it("can compile", function(this: Mocha.ITestCallbackContext) {
this.timeout(800_000); // Allow long timeouts for RWC compilations
let opts: ts.ParsedCommandLine;
let opts!: ts.ParsedCommandLine;
const ioLog: IoLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)), Harness.IO, `internal/cases/rwc/${baseName}`);
const ioLog: IoLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)!), Harness.IO, `internal/cases/rwc/${baseName}`);
currentDirectory = ioLog.currentDirectory;
useCustomLibraryFile = ioLog.useCustomLibraryFile;
useCustomLibraryFile = !!ioLog.useCustomLibraryFile;
runWithIOLog(ioLog, () => {
opts = ts.parseCommandLine(ioLog.arguments, fileName => Harness.IO.readFile(fileName));
assert.equal(opts.errors.length, 0);
@@ -89,7 +89,7 @@ namespace RWC {
const uniqueNames = ts.createMap<true>();
for (const fileName of fileNames) {
// Must maintain order, build result list while checking map
const normalized = ts.normalizeSlashes(Harness.IO.resolvePath(fileName));
const normalized = ts.normalizeSlashes(Harness.IO.resolvePath(fileName)!);
if (!uniqueNames.has(normalized)) {
uniqueNames.set(normalized, true);
// Load the file
@@ -99,7 +99,7 @@ namespace RWC {
// Add files to compilation
for (const fileRead of ioLog.filesRead) {
const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileRead.path));
const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileRead.path)!);
if (!uniqueNames.has(unitName) && !Harness.isDefaultLibraryFile(fileRead.path)) {
uniqueNames.set(unitName, true);
otherFiles.push(getHarnessCompilerInputUnit(unitName));
@@ -134,13 +134,13 @@ namespace RWC {
compilerOptions = compilerResult.options;
function getHarnessCompilerInputUnit(fileName: string): Harness.Compiler.TestFile {
const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileName));
const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileName)!);
let content: string;
try {
content = Harness.IO.readFile(unitName);
content = Harness.IO.readFile(unitName)!;
}
catch (e) {
content = Harness.IO.readFile(fileName);
content = Harness.IO.readFile(fileName)!;
}
return { unitName, content };
}
@@ -196,11 +196,11 @@ namespace RWC {
}
const declContext = Harness.Compiler.prepareDeclarationCompilationContext(
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined!, compilerOptions, currentDirectory // TODO: GH#18217
);
// Reset compilerResult before calling into `compileDeclarationFiles` so the memory from the original compilation can be freed
compilerResult = undefined;
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declContext);
compilerResult = undefined!;
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declContext)!;
return Harness.Compiler.iterateErrorBaseline(tsconfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.diagnostics);
}, baselineOpts);
+15 -14
View File
@@ -19,16 +19,16 @@ namespace Harness.SourceMapRecorder {
interface SourceMapSpanWithDecodeErrors {
sourceMapSpan: ts.SourceMapSpan;
decodeErrors: string[];
decodeErrors: string[] | undefined;
}
namespace SourceMapDecoder {
let sourceMapMappings: string;
let sourceMapNames: string[];
let sourceMapNames: string[] | undefined;
let decodingIndex: number;
let prevNameIndex: number;
let decodeOfEncodedMapping: ts.SourceMapSpan;
let errorDecodeOfEncodedMapping: string;
let errorDecodeOfEncodedMapping: string | undefined;
export function initializeSourceMapDecoding(sourceMapData: ts.SourceMapData) {
sourceMapMappings = sourceMapData.sourceMapMappings;
@@ -88,7 +88,7 @@ namespace Harness.SourceMapRecorder {
for (; moreDigits; decodingIndex++) {
if (createErrorIfCondition(decodingIndex >= sourceMapMappings.length, "Error in decoding base64VLQFormatDecode, past the mapping string")) {
return;
return undefined!; // TODO: GH#18217
}
// 6 digit number
@@ -176,7 +176,7 @@ namespace Harness.SourceMapRecorder {
prevNameIndex += base64VLQFormatDecode();
decodeOfEncodedMapping.nameIndex = prevNameIndex;
// Incorrect nameIndex dont support this map
if (createErrorIfCondition(decodeOfEncodedMapping.nameIndex < 0 || decodeOfEncodedMapping.nameIndex >= sourceMapNames.length, "Invalid name index for the source map entry")) {
if (createErrorIfCondition(decodeOfEncodedMapping.nameIndex < 0 || decodeOfEncodedMapping.nameIndex >= sourceMapNames!.length, "Invalid name index for the source map entry")) {
return { error: errorDecodeOfEncodedMapping, sourceMapSpan: decodeOfEncodedMapping };
}
}
@@ -190,6 +190,7 @@ namespace Harness.SourceMapRecorder {
}
createErrorIfCondition(/*condition*/ true, "No encoded entry found");
return undefined!; // TODO: GH#18217
}
export function hasCompletedDecoding() {
@@ -204,7 +205,7 @@ namespace Harness.SourceMapRecorder {
namespace SourceMapSpanWriter {
let sourceMapRecorder: Compiler.WriterAggregator;
let sourceMapSources: string[];
let sourceMapNames: string[];
let sourceMapNames: string[] | undefined;
let jsFile: documents.TextDocument;
let jsLineMap: ReadonlyArray<number>;
@@ -244,8 +245,8 @@ namespace Harness.SourceMapRecorder {
function getSourceMapSpanString(mapEntry: ts.SourceMapSpan, getAbsentNameIndex?: boolean) {
let mapString = "Emitted(" + mapEntry.emittedLine + ", " + mapEntry.emittedColumn + ") Source(" + mapEntry.sourceLine + ", " + mapEntry.sourceColumn + ") + SourceIndex(" + mapEntry.sourceIndex + ")";
if (mapEntry.nameIndex >= 0 && mapEntry.nameIndex < sourceMapNames.length) {
mapString += " name (" + sourceMapNames[mapEntry.nameIndex] + ")";
if (mapEntry.nameIndex! >= 0 && mapEntry.nameIndex! < sourceMapNames!.length) {
mapString += " name (" + sourceMapNames![mapEntry.nameIndex!] + ")";
}
else {
if ((mapEntry.nameIndex && mapEntry.nameIndex !== -1) || getAbsentNameIndex) {
@@ -259,7 +260,7 @@ namespace Harness.SourceMapRecorder {
export function recordSourceMapSpan(sourceMapSpan: ts.SourceMapSpan) {
// verify the decoded span is same as the new span
const decodeResult = SourceMapDecoder.decodeNextEncodedSourceMapSpan();
let decodeErrors: string[];
let decodeErrors: string[] | undefined;
if (decodeResult.error
|| decodeResult.sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine
|| decodeResult.sourceMapSpan.emittedColumn !== sourceMapSpan.emittedColumn
@@ -345,7 +346,7 @@ namespace Harness.SourceMapRecorder {
return markerId;
}
let prevEmittedCol: number;
let prevEmittedCol!: number;
function iterateSpans(fn: (currentSpan: SourceMapSpanWithDecodeErrors, index: number) => void) {
prevEmittedCol = 1;
for (let i = 0; i < spansOnSingleLine.length; i++) {
@@ -361,7 +362,7 @@ namespace Harness.SourceMapRecorder {
}
}
function writeSourceMapMarker(currentSpan: SourceMapSpanWithDecodeErrors, index: number, endColumn = currentSpan.sourceMapSpan.emittedColumn, endContinues?: boolean) {
function writeSourceMapMarker(currentSpan: SourceMapSpanWithDecodeErrors, index: number, endColumn = currentSpan.sourceMapSpan.emittedColumn, endContinues = false) {
const markerId = getMarkerId(index);
markerIds.push(markerId);
@@ -421,7 +422,7 @@ namespace Harness.SourceMapRecorder {
const jsFileText = getTextOfLine(currentJsLine, jsLineMap, jsFile.text);
if (prevEmittedCol < jsFileText.length) {
// There is remaining text on this line that will be part of next source span so write marker that continues
writeSourceMapMarker(/*currentSpan*/ undefined, spansOnSingleLine.length, /*endColumn*/ jsFileText.length, /*endContinues*/ true);
writeSourceMapMarker(/*currentSpan*/ undefined!, spansOnSingleLine.length, /*endColumn*/ jsFileText.length, /*endContinues*/ true); // TODO: GH#18217
}
// Emit Source text
@@ -440,7 +441,7 @@ namespace Harness.SourceMapRecorder {
for (let i = 0; i < sourceMapDataList.length; i++) {
const sourceMapData = sourceMapDataList[i];
let prevSourceFile: ts.SourceFile;
let prevSourceFile: ts.SourceFile | undefined;
let currentFile: documents.TextDocument;
if (ts.endsWith(sourceMapData.sourceMapFile, ts.Extension.Dts)) {
if (sourceMapDataList.length > jsFiles.length) {
@@ -461,7 +462,7 @@ namespace Harness.SourceMapRecorder {
SourceMapSpanWriter.initializeSourceMapSpanWriter(sourceMapRecorder, sourceMapData, currentFile);
for (const decodedSourceMapping of sourceMapData.sourceMapDecodedMappings) {
const currentSourceFile = program.getSourceFile(sourceMapData.inputSourceFileNames[decodedSourceMapping.sourceIndex]);
const currentSourceFile = program.getSourceFile(sourceMapData.inputSourceFileNames[decodedSourceMapping.sourceIndex])!;
if (currentSourceFile !== prevSourceFile) {
SourceMapSpanWriter.recordNewSourceFileSpan(decodedSourceMapping, currentSourceFile.text);
prevSourceFile = currentSourceFile;
+6 -6
View File
@@ -8,7 +8,7 @@ class Test262BaselineRunner extends RunnerBase {
private static readonly helpersFilePath = "tests/cases/test262-harness/helpers.d.ts";
private static readonly helperFile: Harness.Compiler.TestFile = {
unitName: Test262BaselineRunner.helpersFilePath,
content: Harness.IO.readFile(Test262BaselineRunner.helpersFilePath),
content: Harness.IO.readFile(Test262BaselineRunner.helpersFilePath)!,
};
private static readonly testFileExtensionRegex = /\.js$/;
private static readonly options: ts.CompilerOptions = {
@@ -36,7 +36,7 @@ class Test262BaselineRunner extends RunnerBase {
};
before(() => {
const content = Harness.IO.readFile(filePath);
const content = Harness.IO.readFile(filePath)!;
const testFilename = ts.removeFileExtension(filePath).replace(/\//g, "_") + ".test";
const testCaseContent = Harness.TestCaseParser.makeUnitsFromTest(content, testFilename);
@@ -49,7 +49,7 @@ class Test262BaselineRunner extends RunnerBase {
testState = {
filename: testFilename,
inputFiles,
compilerResult: undefined,
compilerResult: undefined!, // TODO: GH#18217
};
testState.compilerResult = Harness.Compiler.compileFiles(
@@ -61,7 +61,7 @@ class Test262BaselineRunner extends RunnerBase {
});
after(() => {
testState = undefined;
testState = undefined!;
});
it("has the expected emitted code", () => {
@@ -83,13 +83,13 @@ class Test262BaselineRunner extends RunnerBase {
});
it("satisfies invariants", () => {
const sourceFile = testState.compilerResult.program.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
Utils.assertInvariants(sourceFile, /*parent:*/ undefined);
});
it("has the expected AST", () => {
Harness.Baseline.runBaseline(testState.filename + ".AST.txt", () => {
const sourceFile = testState.compilerResult.program.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename))!;
return Utils.sourceFileToJSON(sourceFile);
}, Test262BaselineRunner.baselineOptions);
});
+3 -3
View File
@@ -34,7 +34,7 @@ class TypeWriterWalker {
}
public *getSymbols(fileName: string): IterableIterator<TypeWriterSymbolResult> {
const sourceFile = this.program.getSourceFile(fileName);
const sourceFile = this.program.getSourceFile(fileName)!;
this.currentSourceFile = sourceFile;
const gen = this.visitNode(sourceFile, /*isSymbolWalk*/ true);
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
@@ -43,7 +43,7 @@ class TypeWriterWalker {
}
public *getTypes(fileName: string): IterableIterator<TypeWriterTypeResult> {
const sourceFile = this.program.getSourceFile(fileName);
const sourceFile = this.program.getSourceFile(fileName)!;
this.currentSourceFile = sourceFile;
const gen = this.visitNode(sourceFile, /*isSymbolWalk*/ false);
for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) {
@@ -69,7 +69,7 @@ class TypeWriterWalker {
}
}
private writeTypeOrSymbol(node: ts.Node, isSymbolWalk: boolean): TypeWriterResult {
private writeTypeOrSymbol(node: ts.Node, isSymbolWalk: boolean): TypeWriterResult | undefined {
const actualPos = ts.skipTrivia(this.currentSourceFile.text, node.pos);
const lineAndCharacter = this.currentSourceFile.getLineAndCharacterOfPosition(actualPos);
const sourceText = ts.getSourceTextOfNodeFromSourceFile(this.currentSourceFile, node);
+1 -1
View File
@@ -120,7 +120,7 @@ namespace ts {
operationWasCancelled = true;
}
assert.equal(cancel, operationWasCancelled);
assert.equal(operationWasCancelled, fileNames.length > cancelAfterEmitLength);
assert.equal(operationWasCancelled, fileNames.length > cancelAfterEmitLength!);
assert.deepEqual(outputFileNames, fileNames.slice(0, cancelAfterEmitLength));
};
}
@@ -10,21 +10,21 @@ namespace ts {
`;
it("can cancel signature help mid-request", () => {
verifyOperationCancelledAfter(file, 4, service => // Two calls are top-level in services, one is the root type, and the second should be for the parameter type
service.getSignatureHelpItems("file.ts", file.lastIndexOf("f")),
service.getSignatureHelpItems("file.ts", file.lastIndexOf("f"))!,
r => assert.exists(r.items[0])
);
});
it("can cancel find all references mid-request", () => {
verifyOperationCancelledAfter(file, 3, service => // Two calls are top-level in services, one is the root type
service.findReferences("file.ts", file.lastIndexOf("o")),
service.findReferences("file.ts", file.lastIndexOf("o"))!,
r => assert.exists(r[0].definition)
);
});
it("can cancel quick info mid-request", () => {
verifyOperationCancelledAfter(file, 1, service => // The LS doesn't do any top-level checks on the token for quickinfo, so the first check is within the checker
service.getQuickInfoAtPosition("file.ts", file.lastIndexOf("o")),
service.getQuickInfoAtPosition("file.ts", file.lastIndexOf("o"))!,
r => assert.exists(r.displayParts)
);
});
@@ -52,7 +52,7 @@ namespace ts {
placeOpenBraceOnNewLineForControlBlocks: false,
};
verifyOperationCancelledAfter(file, 1, service => // The LS doesn't do any top-level checks on the token for completion entry details, so the first check is within the checker
service.getCompletionEntryDetails("file.ts", file.lastIndexOf("f"), "foo", options, /*content*/ undefined, {}),
service.getCompletionEntryDetails("file.ts", file.lastIndexOf("f"), "foo", options, /*content*/ undefined, {})!,
r => assert.exists(r.displayParts)
);
});
+3 -3
View File
@@ -601,7 +601,7 @@ namespace ts.projectSystem {
const expectedOutFileName = "/a/b/dist.js";
assert.isTrue(host.fileExists(expectedOutFileName));
const outFileContent = host.readFile(expectedOutFileName);
const outFileContent = host.readFile(expectedOutFileName)!;
assert.isTrue(outFileContent.indexOf(file1.content) !== -1);
assert.isTrue(outFileContent.indexOf(file2.content) === -1);
assert.isTrue(outFileContent.indexOf(file3.content) === -1);
@@ -635,14 +635,14 @@ namespace ts.projectSystem {
// Verify js file
const expectedOutFileName = "/root/TypeScriptProject3/TypeScriptProject3/" + outFileName;
assert.isTrue(host.fileExists(expectedOutFileName));
const outFileContent = host.readFile(expectedOutFileName);
const outFileContent = host.readFile(expectedOutFileName)!;
verifyContentHasString(outFileContent, file1.content);
verifyContentHasString(outFileContent, `//# ${"sourceMappingURL"}=${outFileName}.map`); // Sometimes tools can sometimes see this line as a source mapping url comment, so we obfuscate it a little
// Verify map file
const expectedMapFileName = expectedOutFileName + ".map";
assert.isTrue(host.fileExists(expectedMapFileName));
const mapFileContent = host.readFile(expectedMapFileName);
const mapFileContent = host.readFile(expectedMapFileName)!;
verifyContentHasString(mapFileContent, `"sources":["${inputFileName}"]`);
function verifyContentHasString(content: string, str: string) {
@@ -11,7 +11,7 @@ namespace ts {
assertTypeAcquisitionWithJsonNode(json, configFileName, expectedResult);
}
function verifyAcquisition(actualTypeAcquisition: TypeAcquisition, expectedResult: ExpectedResult) {
function verifyAcquisition(actualTypeAcquisition: TypeAcquisition | undefined, expectedResult: ExpectedResult) {
const parsedTypeAcquisition = JSON.stringify(actualTypeAcquisition);
const expectedTypeAcquisition = JSON.stringify(expectedResult.typeAcquisition);
assert.equal(parsedTypeAcquisition, expectedTypeAcquisition);
@@ -120,7 +120,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
messageText: undefined
messageText: undefined!, // TODO: GH#18217
}
]
});
@@ -215,7 +215,7 @@ namespace ts {
file: undefined,
start: 0,
length: 0,
messageText: undefined
messageText: undefined!, // TODO: GH#18217
}
]
});
+1 -1
View File
@@ -17,7 +17,7 @@ namespace ts {
useCaseSensitiveFileNames: () => true,
getNewLine: () => "\n",
fileExists: (fileName) => fileMap.has(fileName),
readFile: (fileName) => fileMap.has(fileName) ? fileMap.get(fileName).text : undefined,
readFile: (fileName) => fileMap.has(fileName) ? fileMap.get(fileName)!.text : undefined,
writeFile: (fileName, text) => outputs.set(fileName, text),
};
+7 -6
View File
@@ -11,7 +11,7 @@ namespace ts {
}
const result = refactor.extractSymbol.getRangeToExtract(file, createTextSpanFromRange(selectionRange));
assert(result.targetRange === undefined, "failure expected");
const sortedErrors = result.errors.map(e => <string>e.messageText).sort();
const sortedErrors = result.errors!.map(e => <string>e.messageText).sort();
assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors");
});
}
@@ -27,13 +27,14 @@ namespace ts {
const expectedRange = t.ranges.get("extracted");
if (expectedRange) {
let pos: number, end: number;
if (isArray(result.targetRange.range)) {
pos = result.targetRange.range[0].getStart(f);
end = lastOrUndefined(result.targetRange.range).getEnd();
const targetRange = result.targetRange!;
if (isArray(targetRange.range)) {
pos = targetRange.range[0].getStart(f);
end = last(targetRange.range).getEnd();
}
else {
pos = result.targetRange.range.getStart(f);
end = result.targetRange.range.getEnd();
pos = targetRange.range.getStart(f);
end = targetRange.range.getEnd();
}
assert.equal(pos, expectedRange.pos, "incorrect pos of range");
assert.equal(end, expectedRange.end, "incorrect end of range");
+11 -11
View File
@@ -34,7 +34,7 @@ namespace ts {
const name = s === e
? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted"
: source.substring(s, e);
activeRanges.push({ name, pos: text.length, end: undefined });
activeRanges.push({ name, pos: text.length, end: undefined! }); // TODO: GH#18217
lastPos = pos;
continue;
}
@@ -45,7 +45,7 @@ namespace ts {
else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) {
text += source.substring(lastPos, pos);
activeRanges[activeRanges.length - 1].end = text.length;
const range = activeRanges.pop();
const range = activeRanges.pop()!;
if (range.name in ranges) {
throw new Error(`Duplicate name of range ${range.name}`);
}
@@ -100,7 +100,7 @@ namespace ts {
export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage, includeLib?: boolean) {
const t = extractTest(text);
const selectionRange = t.ranges.get("selection");
const selectionRange = t.ranges.get("selection")!;
if (!selectionRange) {
throw new Error(`Test ${caption} does not specify selection range`);
}
@@ -118,7 +118,7 @@ namespace ts {
return;
}
const sourceFile = program.getSourceFile(path);
const sourceFile = program.getSourceFile(path)!;
const context: RefactorContext = {
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
program,
@@ -131,15 +131,15 @@ namespace ts {
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
const infos = refactor.extractSymbol.getAvailableActions(context);
const actions = find(infos, info => info.description === description.message).actions;
const infos = refactor.extractSymbol.getAvailableActions(context)!;
const actions = find(infos, info => info.description === description.message)!.actions;
Harness.Baseline.runBaseline(`${baselineFolder}/${caption}${extension}`, () => {
const data: string[] = [];
data.push(`// ==ORIGINAL==`);
data.push(text.replace("[#|", "/*[#|*/").replace("|]", "/*|]*/"));
for (const action of actions) {
const { renameLocation, edits } = refactor.extractSymbol.getEditsForAction(context, action.name);
const { renameLocation, edits } = refactor.extractSymbol.getEditsForAction(context, action.name)!;
assert.lengthOf(edits, 1);
data.push(`// ==SCOPE::${action.description}==`);
const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges);
@@ -157,7 +157,7 @@ namespace ts {
const host = projectSystem.createServerHost(includeLib ? [f, projectSystem.libFile] : [f]); // libFile is expensive to parse repeatedly - only test when required
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
const program = projectService.inferredProjects[0].getLanguageService().getProgram()!;
return program;
}
@@ -181,8 +181,8 @@ namespace ts {
const host = projectSystem.createServerHost([f, projectSystem.libFile]);
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
const sourceFile = program.getSourceFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram()!;
const sourceFile = program.getSourceFile(f.path)!;
const context: RefactorContext = {
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
program,
@@ -195,7 +195,7 @@ namespace ts {
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
const infos = refactor.extractSymbol.getAvailableActions(context);
const infos = refactor.extractSymbol.getAvailableActions(context)!;
assert.isUndefined(find(infos, info => info.description === description.message));
});
}
+1 -1
View File
@@ -2,7 +2,7 @@
namespace ts {
describe("hostNewLineSupport", () => {
function testLSWithFiles(settings: CompilerOptions, files: Harness.Compiler.TestFile[]) {
function snapFor(path: string): IScriptSnapshot {
function snapFor(path: string): IScriptSnapshot | undefined {
if (path === "lib.d.ts") {
return ScriptSnapshot.fromString("");
}
+4 -4
View File
@@ -10,7 +10,7 @@ namespace ts {
assert.isTrue(typeAndDiagnostics && typeAndDiagnostics.diagnostics.length === 0, "no errors issued");
Harness.Baseline.runBaseline("JSDocParsing/TypeExpressions.parsesCorrectly." + name + ".json",
() => Utils.sourceFileToJSON(typeAndDiagnostics.jsDocTypeExpression.type));
() => Utils.sourceFileToJSON(typeAndDiagnostics!.jsDocTypeExpression.type));
});
}
@@ -89,7 +89,7 @@ namespace ts {
describe("DocComments", () => {
function parsesCorrectly(name: string, content: string) {
it(name, () => {
const comment = parseIsolatedJSDocComment(content);
const comment = parseIsolatedJSDocComment(content)!;
if (!comment) {
Debug.fail("Comment failed to parse entirely");
}
@@ -320,7 +320,7 @@ namespace ts {
assert.equal(root.kind, SyntaxKind.SourceFile);
const first = root.getFirstToken();
assert.isDefined(first);
assert.equal(first.kind, SyntaxKind.VarKeyword);
assert.equal(first!.kind, SyntaxKind.VarKeyword);
});
});
describe("getLastToken", () => {
@@ -329,7 +329,7 @@ namespace ts {
assert.isDefined(root);
const last = root.getLastToken();
assert.isDefined(last);
assert.equal(last.kind, SyntaxKind.EndOfFileToken);
assert.equal(last!.kind, SyntaxKind.EndOfFileToken);
});
});
});
+9 -12
View File
@@ -1,7 +1,7 @@
/// <reference path="..\harness.ts" />
namespace ts {
export function checkResolvedModule(expected: ResolvedModuleFull, actual: ResolvedModuleFull): boolean {
export function checkResolvedModule(expected: ResolvedModuleFull | undefined, actual: ResolvedModuleFull): boolean {
if (!expected === !actual) {
if (expected) {
assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`);
@@ -71,7 +71,7 @@ namespace ts {
return file && file.content;
}
function realpath(path: string): string {
return map.get(path).name;
return map.get(path)!.name;
}
}
@@ -332,7 +332,7 @@ namespace ts {
getSourceFile: (fileName: string, languageVersion: ScriptTarget) => {
const path = normalizePath(combinePaths(currentDirectory, fileName));
const file = files.get(path);
return file && createSourceFile(fileName, file, languageVersion);
return file ? createSourceFile(fileName, file, languageVersion) : undefined;
},
getDefaultLibFileName: () => "lib.d.ts",
writeFile: notImplemented,
@@ -420,7 +420,7 @@ export = C;
}
const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
const file = files.get(path);
return file && createSourceFile(fileName, file, languageVersion);
return file ? createSourceFile(fileName, file, languageVersion) : undefined;
},
getDefaultLibFileName: () => "lib.d.ts",
writeFile: notImplemented,
@@ -975,9 +975,9 @@ import b = require("./moduleB");
function test(typesRoot: string, typeDirective: string, primary: boolean, initialFile: File, targetFile: File, ...otherFiles: File[]) {
const host = createModuleResolutionHost(/*hasDirectoryExists*/ false, ...[initialFile, targetFile].concat(...otherFiles));
const result = resolveTypeReferenceDirective(typeDirective, initialFile.name, { typeRoots: [typesRoot] }, host);
assert(result.resolvedTypeReferenceDirective.resolvedFileName !== undefined, "expected type directive to be resolved");
assert.equal(result.resolvedTypeReferenceDirective.resolvedFileName, targetFile.name, "unexpected result of type reference resolution");
assert.equal(result.resolvedTypeReferenceDirective.primary, primary, "unexpected 'primary' value");
assert(result.resolvedTypeReferenceDirective!.resolvedFileName !== undefined, "expected type directive to be resolved");
assert.equal(result.resolvedTypeReferenceDirective!.resolvedFileName, targetFile.name, "unexpected result of type reference resolution");
assert.equal(result.resolvedTypeReferenceDirective!.primary, primary, "unexpected 'primary' value");
}
it("Can be resolved from primary location", () => {
@@ -1111,10 +1111,7 @@ import b = require("./moduleB");
getNewLine: () => "\r\n",
useCaseSensitiveFileNames: () => false,
readFile: fileName => fileName === file.fileName ? file.text : undefined,
resolveModuleNames() {
assert(false, "resolveModuleNames should not be called");
return undefined;
}
resolveModuleNames: notImplemented,
};
createProgram([f.name], {}, compilerHost);
});
@@ -1145,7 +1142,7 @@ import b = require("./moduleB");
readFile: fileName => fileName === file.fileName ? file.text : undefined,
resolveModuleNames(moduleNames: string[], _containingFile: string) {
assert.deepEqual(moduleNames, ["fs"]);
return [undefined];
return [undefined!]; // TODO: GH#18217
}
};
createProgram([f.name], {}, compilerHost);
+6 -6
View File
@@ -136,7 +136,7 @@ namespace ts.projectSystem {
projectService.openClientFile(file1.path);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
const configuredProject = find(projectService.synchronizeProjectList([]), f => f.info!.projectName === corruptedConfig.path)!;
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors();
@@ -144,13 +144,13 @@ namespace ts.projectSystem {
"'{' expected."
]);
assert.isNotNull(projectErrors[0].file);
assert.equal(projectErrors[0].file.fileName, corruptedConfig.path);
assert.equal(projectErrors[0].file!.fileName, corruptedConfig.path);
}
// fix config and trigger watcher
host.reloadFS([file1, file2, correctConfig]);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
const configuredProject = find(projectService.synchronizeProjectList([]), f => f.info!.projectName === corruptedConfig.path)!;
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors();
@@ -181,7 +181,7 @@ namespace ts.projectSystem {
projectService.openClientFile(file1.path);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
const configuredProject = find(projectService.synchronizeProjectList([]), f => f.info!.projectName === corruptedConfig.path)!;
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors();
@@ -191,7 +191,7 @@ namespace ts.projectSystem {
host.reloadFS([file1, file2, corruptedConfig]);
{
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
const configuredProject = find(projectService.synchronizeProjectList([]), f => f.info!.projectName === corruptedConfig.path)!;
assert.isTrue(configuredProject !== undefined, "should find configured project");
checkProjectErrors(configuredProject, []);
const projectErrors = configuredProjectAt(projectService, 0).getAllProjectErrors();
@@ -199,7 +199,7 @@ namespace ts.projectSystem {
"'{' expected."
]);
assert.isNotNull(projectErrors[0].file);
assert.equal(projectErrors[0].file.fileName, corruptedConfig.path);
assert.equal(projectErrors[0].file!.fileName, corruptedConfig.path);
}
});
});
+1 -1
View File
@@ -77,7 +77,7 @@ namespace ts {
}
}
const vfsys = new vfs.FileSystem(false, { files: { "/lib.d.ts": TestFSWithWatch.libFile.content! } });
const vfsys = new vfs.FileSystem(false, { files: { "/lib.d.ts": TestFSWithWatch.libFile.content } });
files.forEach((v, k) => {
vfsys.mkdirpSync(getDirectoryPath(k));
vfsys.writeFileSync(k, v);
+1 -1
View File
@@ -6,7 +6,7 @@ describe("Public APIs", () => {
const api = `api/${fileName}`;
let fileContent: string;
before(() => {
fileContent = Harness.IO.readFile(builtFile);
fileContent = Harness.IO.readFile(builtFile)!;
});
it("should be acknowledged when they change", () => {
+15 -15
View File
@@ -93,7 +93,7 @@ namespace ts {
newLength = this.program.length;
break;
default:
Debug.assert(false, "Unexpected change");
return Debug.fail("Unexpected change");
}
return createTextChangeRange(oldSpan, newLength);
@@ -114,7 +114,7 @@ namespace ts {
if (oldFile && oldFile.redirectInfo) {
oldFile = oldFile.redirectInfo.unredirected;
}
if (oldFile && oldFile.sourceText.getVersion() === t.text.getVersion()) {
if (oldFile && oldFile.sourceText!.getVersion() === t.text.getVersion()) {
return oldFile;
}
}
@@ -126,7 +126,7 @@ namespace ts {
trace: s => trace.push(s),
getTrace: () => trace,
getSourceFile(fileName): SourceFile {
return files.get(fileName);
return files.get(fileName)!;
},
getDefaultLibFileName(): string {
return "lib.d.ts";
@@ -156,7 +156,7 @@ namespace ts {
}
export function newProgram(texts: NamedSourceText[], rootNames: string[], options: CompilerOptions): ProgramWithSourceTexts {
const host = createTestCompilerHost(texts, options.target);
const host = createTestCompilerHost(texts, options.target!);
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host);
program.sourceTexts = texts;
program.host = host;
@@ -165,10 +165,10 @@ namespace ts {
export function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: ReadonlyArray<string>, options: CompilerOptions, updater: (files: NamedSourceText[]) => void, newTexts?: NamedSourceText[]) {
if (!newTexts) {
newTexts = oldProgram.sourceTexts.slice(0);
newTexts = oldProgram.sourceTexts!.slice(0);
}
updater(newTexts);
const host = createTestCompilerHost(newTexts, options.target, oldProgram);
const host = createTestCompilerHost(newTexts, options.target!, oldProgram);
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
program.sourceTexts = newTexts;
program.host = host;
@@ -191,16 +191,16 @@ namespace ts {
return false;
}
function checkCache<T>(caption: string, program: Program, fileName: string, expectedContent: Map<T>, getCache: (f: SourceFile) => Map<T>, entryChecker: (expected: T, original: T) => boolean): void {
function checkCache<T>(caption: string, program: Program, fileName: string, expectedContent: Map<T> | undefined, getCache: (f: SourceFile) => Map<T> | undefined, entryChecker: (expected: T, original: T) => boolean): void {
const file = program.getSourceFile(fileName);
assert.isTrue(file !== undefined, `cannot find file ${fileName}`);
const cache = getCache(file);
const cache = getCache(file!);
if (expectedContent === undefined) {
assert.isTrue(cache === undefined, `expected ${caption} to be undefined`);
}
else {
assert.isTrue(cache !== undefined, `expected ${caption} to be set`);
assert.isTrue(mapsAreEqual(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`);
assert.isTrue(mapsAreEqual(expectedContent, cache!, entryChecker), `contents of ${caption} did not match the expected contents.`);
}
}
@@ -210,7 +210,7 @@ namespace ts {
if (!left || !right) return false;
const someInLeftHasNoMatch = forEachEntry(left, (leftValue, leftKey) => {
if (!right.has(leftKey)) return true;
const rightValue = right.get(leftKey);
const rightValue = right.get(leftKey)!;
return !(valuesAreEqual ? valuesAreEqual(leftValue, rightValue) : leftValue === rightValue);
});
if (someInLeftHasNoMatch) return false;
@@ -218,11 +218,11 @@ namespace ts {
return !someInRightHasNoMatch;
}
function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map<ResolvedModule>): void {
function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map<ResolvedModule | undefined> | undefined): void {
checkCache("resolved modules", program, fileName, expectedContent, f => f.resolvedModules, checkResolvedModule);
}
function checkResolvedTypeDirectivesCache(program: Program, fileName: string, expectedContent: Map<ResolvedTypeReferenceDirective>): void {
function checkResolvedTypeDirectivesCache(program: Program, fileName: string, expectedContent: Map<ResolvedTypeReferenceDirective> | undefined): void {
checkCache("resolved type directives", program, fileName, expectedContent, f => f.resolvedTypeReferenceDirectiveNames, checkResolvedTypeDirective);
}
@@ -399,7 +399,7 @@ namespace ts {
const program2 = updateProgram(program1, ["/a.ts"], options, files => {
files[0].text = files[0].text.updateProgram('import * as aa from "a";');
});
assert.isDefined(program2.getSourceFile("/a.ts").resolvedModules.get("a"), "'a' is not an unresolved module after re-use");
assert.isDefined(program2.getSourceFile("/a.ts")!.resolvedModules!.get("a"), "'a' is not an unresolved module after re-use");
});
it("resolved type directives cache follows type directives", () => {
@@ -896,7 +896,7 @@ namespace ts {
) {
const actual = isProgramUptoDate(
program, newRootFileNames, newOptions,
path => program.getSourceFileByPath(path).version, /*fileExists*/ returnFalse,
path => program.getSourceFileByPath(path)!.version, /*fileExists*/ returnFalse,
/*hasInvalidatedResolution*/ returnFalse,
/*hasChangedAutomaticTypeDirectiveNames*/ false
);
@@ -916,7 +916,7 @@ namespace ts {
function verifyProgramWithConfigFile(system: System, configFileName: string) {
const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, system)).getCurrentProgram().getProgram();
const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, system, notImplemented);
const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, system, notImplemented)!; // TODO: GH#18217
verifyProgramIsUptoDate(program, fileNames, options);
}
@@ -32,7 +32,7 @@ describe("Colorization", () => {
function identifier(text: string, position?: number) { return createClassification(text, ts.TokenClass.Identifier, position); }
function numberLiteral(text: string, position?: number) { return createClassification(text, ts.TokenClass.NumberLiteral, position); }
function stringLiteral(text: string, position?: number) { return createClassification(text, ts.TokenClass.StringLiteral, position); }
function finalEndOfLineState(value: number): ClassificationEntry { return { value, classification: undefined, position: 0 }; }
function finalEndOfLineState(value: number): ClassificationEntry { return { value, classification: undefined!, position: 0 }; } // TODO: GH#18217
function createClassification(value: string, classification: ts.TokenClass, position?: number): ClassificationEntry {
return { value, classification, position };
}
@@ -48,7 +48,7 @@ describe("Colorization", () => {
const actualEntryPosition = expectedEntry.position !== undefined ? expectedEntry.position : text.indexOf(expectedEntry.value);
assert(actualEntryPosition >= 0, "token: '" + expectedEntry.value + "' does not exit in text: '" + text + "'.");
const actualEntry = getEntryAtPosition(result, actualEntryPosition);
const actualEntry = getEntryAtPosition(result, actualEntryPosition)!;
assert(actualEntry, "Could not find classification entry for '" + expectedEntry.value + "' at position: " + actualEntryPosition);
assert.equal(actualEntry.classification, expectedEntry.classification, "Classification class does not match expected. Expected: " + ts.TokenClass[expectedEntry.classification] + ", Actual: " + ts.TokenClass[actualEntry.classification]);
@@ -351,7 +351,7 @@ describe("Colorization", () => {
pos += lastLength;
lastLength = val.length;
}
return ts.lastOrUndefined(vals);
return ts.last(vals);
}
});
@@ -321,7 +321,7 @@ describe("PatternMatcher", () => {
});
function assertSegmentMatch(candidate: string, pattern: string, expected: ts.PatternMatch | undefined): void {
assert.deepEqual(ts.createPatternMatcher(pattern).getMatchForLastSegmentOfPattern(candidate), expected);
assert.deepEqual(ts.createPatternMatcher(pattern)!.getMatchForLastSegmentOfPattern(candidate), expected);
}
function assertInvalidPattern(pattern: string) {
@@ -329,7 +329,7 @@ describe("PatternMatcher", () => {
}
function assertFullMatch(dottedContainer: string, candidate: string, pattern: string, expected: ts.PatternMatch | undefined): void {
assert.deepEqual(ts.createPatternMatcher(pattern).getFullMatch(dottedContainer.split("."), candidate), expected);
assert.deepEqual(ts.createPatternMatcher(pattern)!.getFullMatch(dottedContainer.split("."), candidate), expected);
}
function spanListToSubstrings(identifier: string, spans: ts.TextSpan[]) {
+8 -8
View File
@@ -12,7 +12,7 @@ namespace ts.server {
write(s): void { lastWrittenToHost = s; },
readFile: () => undefined,
writeFile: noop,
resolvePath(): string { return void 0; },
resolvePath(): string { return undefined!; }, // TODO: GH#18217
fileExists: () => false,
directoryExists: () => false,
getDirectories: () => [],
@@ -47,7 +47,7 @@ namespace ts.server {
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
typingsInstaller: undefined!, // TODO: GH#18217
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: projectSystem.nullLogger,
@@ -81,7 +81,7 @@ namespace ts.server {
seq: 0,
type: "request",
arguments: {
file: undefined
file: undefined! // TODO: GH#18217
}
};
@@ -441,7 +441,7 @@ namespace ts.server {
lastSent: protocol.Message;
private exceptionRaisingHandler(_request: protocol.Request): { response?: any, responseRequired: boolean } {
f1();
return;
return Debug.fail(); // unreachable, throw to make compiler happy
function f1() {
throw new Error("myMessage");
}
@@ -453,7 +453,7 @@ namespace ts.server {
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
typingsInstaller: undefined!, // TODO: GH#18217
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: projectSystem.nullLogger,
@@ -500,7 +500,7 @@ namespace ts.server {
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
typingsInstaller: undefined!, // TODO: GH#18217
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: projectSystem.nullLogger,
@@ -568,7 +568,7 @@ namespace ts.server {
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
typingsInstaller: undefined!, // TODO: GH#18217
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: projectSystem.nullLogger,
@@ -604,7 +604,7 @@ namespace ts.server {
consumeQueue() {
while (this.queue.length > 0) {
const elem = this.queue.pop();
const elem = this.queue.pop()!;
this.handleRequest(elem);
}
}
+2 -2
View File
@@ -8,8 +8,8 @@ namespace ts {
unitName: "main.ts",
content: source
}], [], {}, {}, "/");
const file = result.program.getSourceFile("main.ts");
const checker = result.program.getTypeChecker();
const file = result.program!.getSourceFile("main.ts")!;
const checker = result.program!.getTypeChecker();
verifier(file, checker);
});
}
+5 -5
View File
@@ -8,9 +8,9 @@
namespace ts {
describe("textChanges", () => {
function findChild(name: string, n: Node) {
return find(n);
return find(n)!;
function find(node: Node): Node {
function find(node: Node): Node | undefined {
if (isDeclaration(node) && node.name && isIdentifier(node.name) && node.name.escapedText === name) {
return node;
}
@@ -88,7 +88,7 @@ namespace M
}
}`;
runSingleFileTest("extractMethodLike", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
const statements = (<FunctionDeclaration>findChild("foo", sourceFile)).body.statements.slice(1);
const statements = (<FunctionDeclaration>findChild("foo", sourceFile)).body!.statements.slice(1);
const newFunction = createFunctionDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
@@ -105,11 +105,11 @@ namespace M
// replace statements with return statement
const newStatement = createReturn(
createCall(
/*expression*/ newFunction.name,
/*expression*/ newFunction.name!,
/*typeArguments*/ undefined,
/*argumentsArray*/ emptyArray
));
changeTracker.replaceNodeRange(sourceFile, statements[0], lastOrUndefined(statements), newStatement, { suffix: newLineCharacter });
changeTracker.replaceNodeRange(sourceFile, statements[0], last(statements), newStatement, { suffix: newLineCharacter });
});
}
{
+2 -2
View File
@@ -139,7 +139,7 @@ namespace ts {
return (sourceFile: SourceFile) => {
const result = getMutableClone(sourceFile);
result.statements = createNodeArray([
createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, "Foo", /*typeParameters*/ undefined, /*heritageClauses*/ undefined, /*members*/ undefined),
createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, "Foo", /*typeParameters*/ undefined, /*heritageClauses*/ undefined, /*members*/ undefined!), // TODO: GH#18217
createModuleDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createIdentifier("Foo"), createModuleBlock([createEmptyStatement()]))
]);
return result;
@@ -266,7 +266,7 @@ namespace ts {
function baselineDeclarationTransform(text: string, opts: TranspileOptions) {
const fs = vfs.createFromFileSystem(Harness.IO, /*caseSensitive*/ true, { documents: [new documents.TextDocument("/.src/index.ts", text)] });
const host = new fakes.CompilerHost(fs, opts.compilerOptions);
const program = createProgram(["/.src/index.ts"], opts.compilerOptions, host);
const program = createProgram(["/.src/index.ts"], opts.compilerOptions!, host);
program.emit(program.getSourceFiles()[1], (p, s, bom) => host.writeFile(p, s, bom), /*cancellationToken*/ undefined, /*onlyDts*/ true, opts.transformers);
return fs.readFileSync("/.src/index.d.ts").toString();
}
+5 -5
View File
@@ -53,20 +53,20 @@ namespace ts {
});
after(() => {
transpileResult = undefined;
oldTranspileResult = undefined;
oldTranspileDiagnostics = undefined;
transpileResult = undefined!;
oldTranspileResult = undefined!;
oldTranspileDiagnostics = undefined!;
});
it("Correct errors for " + justName, () => {
Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".errors.txt"), () => {
if (transpileResult.diagnostics.length === 0) {
if (transpileResult.diagnostics!.length === 0) {
/* tslint:disable:no-null-keyword */
return null;
/* tslint:enable:no-null-keyword */
}
return Harness.Compiler.getErrorBaseline(toBeCompiled, transpileResult.diagnostics);
return Harness.Compiler.getErrorBaseline(toBeCompiled, transpileResult.diagnostics!);
});
});
+13 -13
View File
@@ -171,7 +171,7 @@ namespace ts.tscWatch {
assert.equal(host.exitCode, expectedExitCode);
}
function getDiagnosticOfFileFrom(file: SourceFile, text: string, start: number, length: number, message: DiagnosticMessage): Diagnostic {
function getDiagnosticOfFileFrom(file: SourceFile | undefined, text: string, start: number | undefined, length: number | undefined, message: DiagnosticMessage): Diagnostic {
return {
file,
start,
@@ -205,7 +205,7 @@ namespace ts.tscWatch {
function getUnknownCompilerOption(program: Program, configFile: File, option: string) {
const quotedOption = `"${option}"`;
return getDiagnosticOfFile(program.getCompilerOptions().configFile, configFile.content.indexOf(quotedOption), quotedOption.length, Diagnostics.Unknown_compiler_option_0, option);
return getDiagnosticOfFile(program.getCompilerOptions().configFile!, configFile.content.indexOf(quotedOption), quotedOption.length, Diagnostics.Unknown_compiler_option_0, option);
}
function getDiagnosticOfFileFromProgram(program: Program, filePath: string, start: number, length: number, message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic {
@@ -215,7 +215,7 @@ namespace ts.tscWatch {
text = formatStringFromArgs(text, arguments, 5);
}
return getDiagnosticOfFileFrom(program.getSourceFileByPath(toPath(filePath, program.getCurrentDirectory(), s => s.toLowerCase())),
return getDiagnosticOfFileFrom(program.getSourceFileByPath(toPath(filePath, program.getCurrentDirectory(), s => s.toLowerCase()))!,
text, start, length, message);
}
@@ -1101,8 +1101,8 @@ namespace ts.tscWatch {
const host = createWatchedSystem(files);
const watch = createWatchOfConfigFile(configFile.path, host);
const errors = () => [
getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"allowJs"'), '"allowJs"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"),
getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"declaration"'), '"declaration"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration")
getDiagnosticOfFile(watch().getCompilerOptions().configFile!, configFile.content.indexOf('"allowJs"'), '"allowJs"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"),
getDiagnosticOfFile(watch().getCompilerOptions().configFile!, configFile.content.indexOf('"declaration"'), '"declaration"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration")
];
const intialErrors = errors();
checkOutputErrorsInitial(host, intialErrors);
@@ -1112,8 +1112,8 @@ namespace ts.tscWatch {
host.runQueuedTimeoutCallbacks();
const nowErrors = errors();
checkOutputErrorsIncremental(host, nowErrors);
assert.equal(nowErrors[0].start, intialErrors[0].start - configFileContentComment.length);
assert.equal(nowErrors[1].start, intialErrors[1].start - configFileContentComment.length);
assert.equal(nowErrors[0].start, intialErrors[0].start! - configFileContentComment.length);
assert.equal(nowErrors[1].start, intialErrors[1].start! - configFileContentComment.length);
});
it("should not trigger recompilation because of program emit", () => {
@@ -1409,7 +1409,7 @@ namespace ts.tscWatch {
}
function getFile(fileName: string) {
return find(files, file => file.path === fileName);
return find(files, file => file.path === fileName)!;
}
function verifyAffectedAllFiles() {
@@ -2252,7 +2252,7 @@ declare module "fs" {
const disableConsoleClear = options.diagnostics || options.extendedDiagnostics || options.preserveWatchOutput;
const host = createWatchedSystem(files);
createWatchOfFilesAndCompilerOptions([file.path], host, options);
checkOutputErrorsInitial(host, emptyArray, disableConsoleClear, options.extendedDiagnostics && [
checkOutputErrorsInitial(host, emptyArray, disableConsoleClear, options.extendedDiagnostics ? [
"Current directory: / CaseSensitiveFileNames: false\n",
"Synchronizing program\n",
"CreatingProgramWith::\n",
@@ -2260,21 +2260,21 @@ declare module "fs" {
" options: {\"extendedDiagnostics\":true}\n",
"FileWatcher:: Added:: WatchInfo: f.ts 250 Source file\n",
"FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 Source file\n"
]);
] : undefined);
file.content = "//";
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkOutputErrorsIncremental(host, emptyArray, disableConsoleClear, options.extendedDiagnostics && [
checkOutputErrorsIncremental(host, emptyArray, disableConsoleClear, options.extendedDiagnostics ? [
"FileWatcher:: Triggered with /f.ts1:: WatchInfo: f.ts 250 Source file\n",
"Scheduling update\n",
"Elapsed:: 0ms FileWatcher:: Triggered with /f.ts1:: WatchInfo: f.ts 250 Source file\n"
], options.extendedDiagnostics && [
] : undefined, options.extendedDiagnostics ? [
"Synchronizing program\n",
"CreatingProgramWith::\n",
" roots: [\"f.ts\"]\n",
" options: {\"extendedDiagnostics\":true}\n"
]);
] : undefined);
}
it("without --diagnostics or --extendedDiagnostics", () => {
+6 -5
View File
@@ -135,11 +135,12 @@ namespace ts {
const parsed = parseConfigFileTextToJson("/apath/tsconfig.json", "invalid");
assert.deepEqual(parsed.config, { invalid: undefined });
const expected = createCompilerDiagnostic(Diagnostics._0_expected, "{");
assert.equal(parsed.error.messageText, expected.messageText);
assert.equal(parsed.error.category, expected.category);
assert.equal(parsed.error.code, expected.code);
assert.equal(parsed.error.start, 0);
assert.equal(parsed.error.length, "invalid".length);
const error = parsed.error!;
assert.equal(error.messageText, expected.messageText);
assert.equal(error.category, expected.category);
assert.equal(error.code, expected.code);
assert.equal(error.start, 0);
assert.equal(error.length, "invalid".length);
});
it("returns object when users correctly specify library", () => {
+80 -84
View File
@@ -58,7 +58,7 @@ namespace ts.projectSystem {
msg: noop,
startGroup: noop,
endGroup: noop,
getLogFileName: (): string => undefined
getLogFileName: () => undefined,
};
export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller {
@@ -189,7 +189,7 @@ namespace ts.projectSystem {
}
getEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"]): T["data"] {
let eventData: T["data"];
let eventData: T["data"] | undefined;
filterMutate(this.events, e => {
if (e.eventName === eventName) {
if (eventData !== undefined) {
@@ -200,8 +200,7 @@ namespace ts.projectSystem {
}
return true;
});
assert.isDefined(eventData);
return eventData;
return Debug.assertDefined(eventData);
}
hasZeroEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"]) {
@@ -216,7 +215,7 @@ namespace ts.projectSystem {
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>, configFile = "/tsconfig.json"): void {
assert.deepEqual<server.ProjectInfoTelemetryEventData>(this.getEvent<server.ProjectInfoTelemetryEvent>(server.ProjectInfoTelemetryEvent), {
projectId: sys.createSHA256Hash(configFile),
projectId: sys.createSHA256Hash!(configFile),
fileStats: fileStats({ ts: 1 }),
compilerOptions: {},
extends: false,
@@ -294,7 +293,7 @@ namespace ts.projectSystem {
cancellationToken: server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
typingsInstaller: undefined!, // TODO: GH#18217
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: opts.logger || nullLogger,
@@ -336,7 +335,7 @@ namespace ts.projectSystem {
const cancellationToken = parameters.cancellationToken || server.nullCancellationToken;
const logger = parameters.logger || nullLogger;
const useSingleInferredProject = parameters.useSingleInferredProject !== undefined ? parameters.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler, options);
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller!, parameters.eventHandler!, options); // TODO: GH#18217
}
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
@@ -404,7 +403,7 @@ namespace ts.projectSystem {
}
function checkOpenFiles(projectService: server.ProjectService, expectedFiles: File[]) {
checkArray("Open files", arrayFrom(projectService.openFiles.keys(), path => projectService.getScriptInfoForPath(path as Path).fileName), expectedFiles.map(file => file.path));
checkArray("Open files", arrayFrom(projectService.openFiles.keys(), path => projectService.getScriptInfoForPath(path as Path)!.fileName), expectedFiles.map(file => file.path));
}
function textSpanFromSubstring(str: string, substring: string): TextSpan {
@@ -420,7 +419,7 @@ namespace ts.projectSystem {
* setRequestToCancel();
*/
export class TestServerCancellationToken implements server.ServerCancellationToken {
private currentId = -1;
private currentId: number | undefined = -1;
private requestToCancel = -1;
private isCancellationRequestedCount = 0;
@@ -708,7 +707,7 @@ namespace ts.projectSystem {
projectService.checkNumberOfProjects({ inferredProjects: 2, configuredProjects: 1 });
assert.isTrue(projectService.inferredProjects[0].isOrphan());
checkProjectActualFiles(projectService.inferredProjects[1], [commonFile2.path, libFile.path]);
checkProjectActualFiles(projectService.configuredProjects.get(configFile.path), [libFile.path, commonFile1.path, configFile.path]);
checkProjectActualFiles(projectService.configuredProjects.get(configFile.path)!, [libFile.path, commonFile1.path, configFile.path]);
checkWatchedFiles(host, watchedFiles);
@@ -1106,15 +1105,15 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
projectService.openExternalProject({ rootFiles: toExternalFiles([file1.path]), options: {}, projectFileName: proj1name });
const proj1 = projectService.findProject(proj1name);
const proj1 = projectService.findProject(proj1name)!;
assert.isTrue(proj1.languageServiceEnabled);
projectService.openExternalProject({ rootFiles: toExternalFiles([file2.path]), options: {}, projectFileName: proj2name });
const proj2 = projectService.findProject(proj2name);
const proj2 = projectService.findProject(proj2name)!;
assert.isTrue(proj2.languageServiceEnabled);
projectService.openExternalProject({ rootFiles: toExternalFiles([file3.path]), options: {}, projectFileName: proj3name });
const proj3 = projectService.findProject(proj3name);
const proj3 = projectService.findProject(proj3name)!;
assert.isFalse(proj3.languageServiceEnabled);
});
@@ -1182,7 +1181,7 @@ namespace ts.projectSystem {
const projectService = createProjectService(host, { useSingleInferredProject: true });
projectService.openClientFile(file1.path);
checkNumberOfConfiguredProjects(projectService, 1);
const project = projectService.configuredProjects.get(configFile.path);
const project = projectService.configuredProjects.get(configFile.path)!;
assert.isTrue(project.hasOpenRef()); // file1
projectService.closeClientFile(file1.path);
@@ -1216,7 +1215,7 @@ namespace ts.projectSystem {
const projectService = createProjectService(host, { useSingleInferredProject: true });
projectService.openClientFile(file1.path);
checkNumberOfConfiguredProjects(projectService, 1);
const project = projectService.configuredProjects.get(configFile.path);
const project = projectService.configuredProjects.get(configFile.path)!;
assert.isTrue(project.hasOpenRef()); // file1
projectService.closeClientFile(file1.path);
@@ -1489,13 +1488,13 @@ namespace ts.projectSystem {
service.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences);
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences)!;
// should contain completions for string
assert.isTrue(completions1.entries.some(e => e.name === "charAt"), "should contain 'charAt'");
assert.isFalse(completions1.entries.some(e => e.name === "toExponential"), "should not contain 'toExponential'");
service.closeClientFile(f2.path);
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences);
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences)!;
// should contain completions for string
assert.isFalse(completions2.entries.some(e => e.name === "charAt"), "should not contain 'charAt'");
assert.isTrue(completions2.entries.some(e => e.name === "toExponential"), "should contain 'toExponential'");
@@ -1521,13 +1520,13 @@ namespace ts.projectSystem {
service.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences);
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences)!;
assert.isTrue(completions1.entries.some(e => e.name === "somelongname"), "should contain 'somelongname'");
service.closeClientFile(f2.path);
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences);
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences)!;
assert.isFalse(completions2.entries.some(e => e.name === "somelongname"), "should not contain 'somelongname'");
const sf2 = service.externalProjects[0].getLanguageService().getProgram().getSourceFile(f2.path);
const sf2 = service.externalProjects[0].getLanguageService().getProgram()!.getSourceFile(f2.path)!;
assert.equal(sf2.text, "");
});
@@ -2048,8 +2047,8 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
projectService.applyChangesInOpenFiles([tsFile], [], []);
const projs = projectService.synchronizeProjectList([]);
projectService.findProject(projs[0].info.projectName).getLanguageService().getNavigationBarItems(tsFile.fileName);
projectService.synchronizeProjectList([projs[0].info]);
projectService.findProject(projs[0].info!.projectName)!.getLanguageService().getNavigationBarItems(tsFile.fileName);
projectService.synchronizeProjectList([projs[0].info!]);
projectService.applyChangesInOpenFiles([jsFile], [], []);
});
@@ -2333,7 +2332,7 @@ namespace ts.projectSystem {
const project = projectService.externalProjects[0];
const scriptInfo = project.getScriptInfo(file1.path);
const scriptInfo = project.getScriptInfo(file1.path)!;
const snap = scriptInfo.getSnapshot();
const actualText = getSnapshotText(snap);
assert.equal(actualText, "", `expected content to be empty string, got "${actualText}"`);
@@ -2341,12 +2340,12 @@ namespace ts.projectSystem {
projectService.openClientFile(file1.path, `var x = 1;`);
project.updateGraph();
const quickInfo = project.getLanguageService().getQuickInfoAtPosition(file1.path, 4);
const quickInfo = project.getLanguageService().getQuickInfoAtPosition(file1.path, 4)!;
assert.equal(quickInfo.kind, ScriptElementKind.variableElement);
projectService.closeClientFile(file1.path);
const scriptInfo2 = project.getScriptInfo(file1.path);
const scriptInfo2 = project.getScriptInfo(file1.path)!;
const actualText2 = getSnapshotText(scriptInfo2.getSnapshot());
assert.equal(actualText2, "", `expected content to be empty string, got "${actualText2}"`);
});
@@ -2391,7 +2390,7 @@ namespace ts.projectSystem {
projectService.openClientFile(file1.path);
projectService.inferredProjects[0].getLanguageService(/*ensureSynchronized*/ false).getOutliningSpans(file1.path);
projectService.setCompilerOptionsForInferredProjects({ target: ScriptTarget.ES5, allowJs: true });
projectService.getScriptInfo(file1.path).editContent(0, 0, " ");
projectService.getScriptInfo(file1.path)!.editContent(0, 0, " ");
projectService.inferredProjects[0].getLanguageService(/*ensureSynchronized*/ false).getOutliningSpans(file1.path);
projectService.closeClientFile(file1.path);
});
@@ -2418,9 +2417,9 @@ namespace ts.projectSystem {
projectService.openClientFile(file2.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const project1 = projectService.configuredProjects.get(tsconfig1.path);
const project1 = projectService.configuredProjects.get(tsconfig1.path)!;
assert.isTrue(project1.hasOpenRef(), "Has open ref count in project1 - 1"); // file2
assert.equal(project1.getScriptInfo(file2.path).containingProjects.length, 1, "containing projects count");
assert.equal(project1.getScriptInfo(file2.path)!.containingProjects.length, 1, "containing projects count");
assert.isFalse(project1.isClosed());
projectService.openClientFile(file1.path);
@@ -2429,12 +2428,12 @@ namespace ts.projectSystem {
assert.strictEqual(projectService.configuredProjects.get(tsconfig1.path), project1);
assert.isFalse(project1.isClosed());
const project2 = projectService.configuredProjects.get(tsconfig2.path);
const project2 = projectService.configuredProjects.get(tsconfig2.path)!;
assert.isTrue(project2.hasOpenRef(), "Has open ref count in project2 - 2"); // file1
assert.isFalse(project2.isClosed());
assert.equal(project1.getScriptInfo(file1.path).containingProjects.length, 2, `${file1.path} containing projects count`);
assert.equal(project1.getScriptInfo(file2.path).containingProjects.length, 1, `${file2.path} containing projects count`);
assert.equal(project1.getScriptInfo(file1.path)!.containingProjects.length, 2, `${file1.path} containing projects count`);
assert.equal(project1.getScriptInfo(file2.path)!.containingProjects.length, 1, `${file2.path} containing projects count`);
projectService.closeClientFile(file2.path);
checkNumberOfProjects(projectService, { configuredProjects: 2 });
@@ -2494,10 +2493,10 @@ namespace ts.projectSystem {
projectService.openClientFile(file3.path);
projectService.openClientFile(file4.path);
const infos = files.map(file => projectService.getScriptInfoForPath(file.path as Path));
const infos = files.map(file => projectService.getScriptInfoForPath(file.path as Path)!);
checkOpenFiles(projectService, files);
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
const configProject1 = projectService.configuredProjects.get(configFile.path);
const configProject1 = projectService.configuredProjects.get(configFile.path)!;
assert.isTrue(configProject1.hasOpenRef()); // file1 and file3
checkProjectActualFiles(configProject1, [file1.path, file3.path, configFile.path]);
const inferredProject1 = projectService.inferredProjects[0];
@@ -2568,7 +2567,7 @@ namespace ts.projectSystem {
function verifyConfiguredProjectStateAfterUpdate(hasOpenRef: boolean, inferredProjects: number) {
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects });
const configProject2 = projectService.configuredProjects.get(configFile.path);
const configProject2 = projectService.configuredProjects.get(configFile.path)!;
assert.strictEqual(configProject2, configProject1);
checkProjectActualFiles(configProject2, [file1.path, file2.path, file3.path, configFile.path]);
assert.equal(configProject2.hasOpenRef(), hasOpenRef);
@@ -2607,7 +2606,7 @@ namespace ts.projectSystem {
projectService.openClientFile(file3.path);
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
const configuredProject = projectService.configuredProjects.get(configFile.path);
const configuredProject = projectService.configuredProjects.get(configFile.path)!;
assert.isTrue(configuredProject.hasOpenRef()); // file1 and file3
checkProjectActualFiles(configuredProject, [file1.path, file3.path, configFile.path]);
const inferredProject1 = projectService.inferredProjects[0];
@@ -2709,7 +2708,7 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
projectService.openClientFile(f1.path);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const project = projectService.configuredProjects.get(config.path);
const project = projectService.configuredProjects.get(config.path)!;
assert.isTrue(project.hasOpenRef()); // f1
assert.isFalse(project.isClosed());
@@ -2721,7 +2720,7 @@ namespace ts.projectSystem {
for (const f of [f1, f2, f3]) {
// All the script infos should be present and contain the project since it is still alive.
const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path));
const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path))!;
assert.equal(scriptInfo.containingProjects.length, 1, `expect 1 containing projects for '${f.path}'`);
assert.equal(scriptInfo.containingProjects[0], project, `expect configured project to be the only containing project for '${f.path}'`);
}
@@ -2764,7 +2763,7 @@ namespace ts.projectSystem {
host.getFileSize = (filePath: string) =>
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
let lastEvent: server.ProjectLanguageServiceStateEvent;
let lastEvent!: server.ProjectLanguageServiceStateEvent;
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
@@ -2816,7 +2815,7 @@ namespace ts.projectSystem {
const originalGetFileSize = host.getFileSize;
host.getFileSize = (filePath: string) =>
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
let lastEvent: server.ProjectLanguageServiceStateEvent;
let lastEvent!: server.ProjectLanguageServiceStateEvent;
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
@@ -2906,7 +2905,7 @@ namespace ts.projectSystem {
host.reloadFS([libFile, site]);
host.checkTimeoutQueueLengthAndRun(1);
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info));
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info!)); // TODO: GH#18217 GH#20039
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
externalProject.rootFiles.length = 1;
@@ -2979,7 +2978,7 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const project = projectService.configuredProjects.get(configFile.path);
const project = projectService.configuredProjects.get(configFile.path)!;
assert.isDefined(project);
checkProjectActualFiles(project, map(files, file => file.path));
checkWatchedFiles(host, mapDefined(files, file => file === file1 ? undefined : file.path));
@@ -3040,7 +3039,7 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const project = projectService.configuredProjects.get(configFile.path);
const project = projectService.configuredProjects.get(configFile.path)!;
assert.isDefined(project);
checkProjectActualFiles(project, [file1.path, libFile.path, module1.path, module2.path, configFile.path]);
checkWatchedFiles(host, [libFile.path, module1.path, module2.path, configFile.path]);
@@ -3145,7 +3144,7 @@ namespace ts.projectSystem {
});
const projectService = session.getProjectService();
const configuredProject = projectService.configuredProjects.get(config.path);
const configuredProject = projectService.configuredProjects.get(config.path)!;
verifyConfiguredProject();
// open files/file1 = should not create another project
@@ -3296,7 +3295,7 @@ namespace ts.projectSystem {
const host = createServerHost(files);
const service = createProjectService(host);
service.openClientFile(file1.path);
checkProjectActualFiles(service.configuredProjects.get(config.path), [file1.path, file2.path, libFile.path, config.path]);
checkProjectActualFiles(service.configuredProjects.get(config.path)!, [file1.path, file2.path, libFile.path, config.path]);
const configContent2 = JSON.stringify({
files: ["src/file1.ts"]
@@ -3305,19 +3304,18 @@ namespace ts.projectSystem {
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkProjectActualFiles(service.configuredProjects.get(config.path), [file1.path, libFile.path, config.path]);
checkProjectActualFiles(service.configuredProjects.get(config.path)!, [file1.path, libFile.path, config.path]);
verifyFile2InfoIsOrphan();
file2.content += "export let z = 10;";
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkProjectActualFiles(service.configuredProjects.get(config.path), [file1.path, libFile.path, config.path]);
checkProjectActualFiles(service.configuredProjects.get(config.path)!, [file1.path, libFile.path, config.path]);
verifyFile2InfoIsOrphan();
function verifyFile2InfoIsOrphan() {
const info = service.getScriptInfoForPath(file2.path as Path);
assert.isDefined(info);
const info = Debug.assertDefined(service.getScriptInfoForPath(file2.path as Path));
assert.equal(info.containingProjects.length, 0);
}
});
@@ -3339,7 +3337,7 @@ namespace ts.projectSystem {
},
startGroup: noop,
endGroup: noop,
getLogFileName: (): string => undefined
getLogFileName: () => undefined
};
return {
errorLogger,
@@ -3362,7 +3360,7 @@ namespace ts.projectSystem {
projectService.openClientFile(file1.path);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const project = projectService.findProject(corruptedConfig.path);
const project = projectService.findProject(corruptedConfig.path)!;
checkProjectRootFiles(project, [file1.path]);
});
@@ -3904,7 +3902,7 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
projectService.openClientFile(f.path);
projectService.checkNumberOfProjects({ configuredProjects: 1 });
const project = projectService.configuredProjects.get(config.path);
const project = projectService.configuredProjects.get(config.path)!;
assert.isTrue(project.hasOpenRef()); // f
projectService.closeClientFile(f.path);
@@ -4053,7 +4051,7 @@ namespace ts.projectSystem {
// force to load the content of the file
p.updateGraph();
const scriptInfo = p.getScriptInfo(f.path);
const scriptInfo = p.getScriptInfo(f.path)!;
checkSnapLength(scriptInfo.getSnapshot(), f.content.length);
// open project and replace its content with empty string
@@ -4137,7 +4135,7 @@ namespace ts.projectSystem {
function verifyProject() {
assert.isDefined(service.configuredProjects.get(configFile.path));
const project = service.configuredProjects.get(configFile.path);
const project = service.configuredProjects.get(configFile.path)!;
checkProjectActualFiles(project, files.map(f => f.path));
}
});
@@ -5037,7 +5035,7 @@ namespace ts.projectSystem {
// verify content
const projectServiice = session.getProjectService();
const snap1 = projectServiice.getScriptInfo(f1.path).getSnapshot();
const snap1 = projectServiice.getScriptInfo(f1.path)!.getSnapshot();
assert.equal(getSnapshotText(snap1), tmp.content, "content should be equal to the content of temp file");
// reload from original file file
@@ -5049,7 +5047,7 @@ namespace ts.projectSystem {
});
// verify content
const snap2 = projectServiice.getScriptInfo(f1.path).getSnapshot();
const snap2 = projectServiice.getScriptInfo(f1.path)!.getSnapshot();
assert.equal(getSnapshotText(snap2), f1.content, "content should be equal to the content of original file");
});
@@ -5074,7 +5072,7 @@ namespace ts.projectSystem {
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const info = projectService.getScriptInfo(f1.path);
const info = projectService.getScriptInfo(f1.path)!;
assert.isDefined(info);
checkScriptInfoContents(openContent, "contents set during open request");
@@ -5647,8 +5645,7 @@ namespace ts.projectSystem {
function verifyConfiguredProject(host: TestServerHost, projectService: TestProjectService, orphanInferredProject?: boolean) {
projectService.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: orphanInferredProject ? 1 : 0 });
const project = projectService.configuredProjects.get(tsconfig.path);
assert.isDefined(project);
const project = Debug.assertDefined(projectService.configuredProjects.get(tsconfig.path));
if (orphanInferredProject) {
const inferredProject = projectService.inferredProjects[0];
@@ -6240,7 +6237,7 @@ namespace ts.projectSystem {
const calledMap = createMultiMap<[U, V, W, X]>();
const cb = (<any>host)[prop].bind(host);
(<any>host)[prop] = (f: string, arg1?: U, arg2?: V, arg3?: W, arg4?: X) => {
calledMap.add(f, [arg1, arg2, arg3, arg4]);
calledMap.add(f, [arg1!, arg2!, arg3!, arg4!]); // TODO: GH#18217
return cb(f, arg1, arg2, arg3, arg4);
};
return calledMap;
@@ -6495,7 +6492,7 @@ namespace ts.projectSystem {
assert.isDefined(configFileName, `should find config`);
checkNumberOfConfiguredProjects(projectService, 1);
const project = projectService.configuredProjects.get(tsconfigFile.path);
const project = projectService.configuredProjects.get(tsconfigFile.path)!;
checkProjectActualFiles(project, map(projectFiles, f => f.path));
const callsTrackingHost = createCallsTrackingHost(host);
@@ -6504,8 +6501,8 @@ namespace ts.projectSystem {
const getDefinitionRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(protocol.CommandTypes.Definition, {
file: clientFile.path,
position: clientFile.content.indexOf("/vessel") + 1,
line: undefined,
offset: undefined
line: undefined!, // TODO: GH#18217
offset: undefined! // TODO: GH#18217
});
const response = session.executeCommand(getDefinitionRequest).response as server.protocol.FileSpan[];
assert.equal(response[0].file, moduleFile.path, "Should go to definition of vessel: response: " + JSON.stringify(response));
@@ -6581,11 +6578,11 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
const canonicalConfigPath = toCanonical(tsconfigFile.path);
const { configFileName } = projectService.openClientFile(file1.path);
assert.equal(configFileName, tsconfigFile.path, `should find config`);
assert.equal(configFileName, tsconfigFile.path as server.NormalizedPath, `should find config`); // tslint:disable-line no-unnecessary-type-assertion (TODO: GH#18217)
checkNumberOfConfiguredProjects(projectService, 1);
const watchingRecursiveDirectories = [`${canonicalFrontendDir}/src`, canonicalFrontendDir].concat(getNodeModuleDirectories(getDirectoryPath(canonicalFrontendDir)));
const project = projectService.configuredProjects.get(canonicalConfigPath);
const project = projectService.configuredProjects.get(canonicalConfigPath)!;
verifyProjectAndWatchedDirectories();
const callsTrackingHost = createCallsTrackingHost(host);
@@ -6664,7 +6661,7 @@ namespace ts.projectSystem {
const service = createProjectService(host);
service.openClientFile(file1.path);
const project = service.configuredProjects.get(tsconfig.path);
const project = service.configuredProjects.get(tsconfig.path)!;
checkProjectActualFiles(project, files.map(f => f.path));
assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file1.path).map(diag => diag.messageText), ["Cannot find module 'debug'."]);
assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(file2.path).map(diag => diag.messageText), ["Cannot find module 'debug'."]);
@@ -6736,7 +6733,7 @@ namespace ts.projectSystem {
const host = createServerHost(projectFiles.concat(otherFiles));
const projectService = createProjectService(host);
const { configFileName } = projectService.openClientFile(app.path);
assert.equal(configFileName, tsconfigJson.path, `should find config`);
assert.equal(configFileName, tsconfigJson.path as server.NormalizedPath, `should find config`); // TODO: GH#18217
const recursiveWatchedDirectories: string[] = [appFolder].concat(getNodeModuleDirectories(getDirectoryPath(appFolder)));
verifyProject();
@@ -6815,7 +6812,7 @@ namespace ts.projectSystem {
});
const lodashIndexPath = root + "/a/b/node_modules/@types/lodash/index.d.ts";
projectFiles.push(find(filesAndFoldersToAdd, f => f.path === lodashIndexPath));
projectFiles.push(find(filesAndFoldersToAdd, f => f.path === lodashIndexPath)!);
// we would now not have failed lookup in the parent of appFolder since lodash is available
recursiveWatchedDirectories.length = 1;
// npm installation complete, timeout after reload fs
@@ -6836,7 +6833,7 @@ namespace ts.projectSystem {
function verifyProject() {
checkNumberOfConfiguredProjects(projectService, 1);
const project = projectService.configuredProjects.get(tsconfigJson.path);
const project = projectService.configuredProjects.get(tsconfigJson.path)!;
const projectFilePaths = map(projectFiles, f => f.path);
checkProjectActualFiles(project, projectFilePaths);
@@ -6872,7 +6869,7 @@ namespace ts.projectSystem {
const service = createProjectService(host);
service.openClientFile(app.path);
const project = service.configuredProjects.get(tsconfig.path);
const project = service.configuredProjects.get(tsconfig.path)!;
checkProjectActualFiles(project, files.map(f => f.path));
assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), ["Cannot find module 'debug'."]);
@@ -7071,7 +7068,7 @@ namespace ts.projectSystem {
content: JSON.stringify(configObj || { compilerOptions: {} })
};
const files = [file1Consumer1, moduleFile1, file1Consumer2, moduleFile2, ...additionalFiles, globalFile3, libFile, configFile];
const files: File[] = [file1Consumer1, moduleFile1, file1Consumer2, moduleFile2, ...additionalFiles, globalFile3, libFile, configFile];
const filesToReload = firstReloadFileList && getFiles(firstReloadFileList) || files;
const host = createServerHost([filesToReload[0], configFile]);
@@ -7097,7 +7094,7 @@ namespace ts.projectSystem {
}
function getFile(fileName: string) {
return find(files, file => file.path === fileName);
return find(files, file => file.path === fileName)!;
}
function verifyNoProjectsUpdatedInBackgroundEvent(filesToReload?: File[]) {
@@ -7345,7 +7342,7 @@ namespace ts.projectSystem {
const projectService = session.getProjectService();
verifyInitialOpen(file1);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const project = projectService.configuredProjects.get(configFile.path);
const project = projectService.configuredProjects.get(configFile.path)!;
verifyProject();
if (limitHit) {
(project as ResolutionCacheHost).maxNumberOfFilesToIterateForInvalidation = 1;
@@ -7521,7 +7518,7 @@ namespace ts.projectSystem {
const host = createServerHost(files, { useWindowsStylePaths: true });
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
const project = projectService.configuredProjects.get(configFile.path);
const project = projectService.configuredProjects.get(configFile.path)!;
assert.isDefined(project);
const winsowsStyleLibFilePath = "c:/" + libFile.path.substring(1);
checkProjectActualFiles(project, files.map(f => f === libFile ? winsowsStyleLibFilePath : f.path));
@@ -7849,7 +7846,7 @@ new C();`
function verifyProjectWithResolvedModule(session: TestSession) {
const projectService = session.getProjectService();
const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath);
const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!;
checkProjectActualFiles(project, filesInProjectWithResolvedModule);
verifyWatchedFilesAndDirectories(session.host, filesInProjectWithResolvedModule, watchedDirectoriesWithResolvedModule);
verifyErrors(session, []);
@@ -7857,7 +7854,7 @@ new C();`
function verifyProjectWithUnresolvedModule(session: TestSession) {
const projectService = session.getProjectService();
const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath);
const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!;
checkProjectActualFiles(project, filesInProjectWithUnresolvedModule);
verifyWatchedFilesAndDirectories(session.host, filesInProjectWithUnresolvedModule, watchedDirectoriesWithUnresolvedModule);
const startOffset = recognizersDateTimeSrcFile.content.indexOf('"') + 1;
@@ -8358,8 +8355,7 @@ new C();`
const projectService = createProjectService(host);
projectService.openClientFile(index.path);
const project = projectService.configuredProjects.get(configFile.path);
assert.isDefined(project);
const project = Debug.assertDefined(projectService.configuredProjects.get(configFile.path));
verifyProjectAndCompletions();
// Add file2
@@ -8377,7 +8373,7 @@ new C();`
verifyProjectAndCompletions();
function verifyProjectAndCompletions() {
const completions = project.getLanguageService().getCompletionsAtPosition(index.path, completionPosition, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
const completions = project.getLanguageService().getCompletionsAtPosition(index.path, completionPosition, { includeExternalModuleExports: false, includeInsertTextCompletions: false })!;
checkArray("Completion Entries", completions.entries.map(e => e.name), expectedCompletions);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
@@ -8443,7 +8439,7 @@ new C();`
};
function getProject(service: TestProjectService) {
return service.configuredProjects.get(configFile.path);
return service.configuredProjects.get(configFile.path)!;
}
function checkProject(service: TestProjectService, moduleIsOrphan: boolean) {
@@ -8451,7 +8447,7 @@ new C();`
const project = getProject(service);
project.getLanguageService();
checkProjectActualFiles(project, [file.path, libFile.path, configFile.path, ...(moduleIsOrphan ? [] : [moduleFile.path])]);
const moduleInfo = service.getScriptInfo(moduleFile.path);
const moduleInfo = service.getScriptInfo(moduleFile.path)!;
assert.isDefined(moduleInfo);
assert.equal(moduleInfo.isOrphan(), moduleIsOrphan);
const key = service.documentRegistry.getKeyForCompilationSettings(project.getCompilationSettings());
@@ -8467,13 +8463,13 @@ new C();`
}
function changeFileToNotImportModule(service: TestProjectService) {
const info = service.getScriptInfo(file.path);
const info = service.getScriptInfo(file.path)!;
service.applyChangesToFile(info, [{ span: { start: 0, length: importModuleContent.length }, newText: "" }]);
checkProject(service, /*moduleIsOrphan*/ true);
}
function changeFileToImportModule(service: TestProjectService) {
const info = service.getScriptInfo(file.path);
const info = service.getScriptInfo(file.path)!;
service.applyChangesToFile(info, [{ span: { start: 0, length: 0 }, newText: importModuleContent }]);
checkProject(service, /*moduleIsOrphan*/ false);
}
@@ -8482,7 +8478,7 @@ new C();`
const { service } = createServiceAndHost();
const project = getProject(service);
const moduleInfo = service.getScriptInfo(moduleFile.path);
const moduleInfo = service.getScriptInfo(moduleFile.path)!;
const sourceFile = moduleInfo.cacheSourceFile.sourceFile;
assert.equal(project.getSourceFile(moduleInfo.path), sourceFile);
@@ -8500,7 +8496,7 @@ new C();`
const { host, service } = createServiceAndHost();
const project = getProject(service);
const moduleInfo = service.getScriptInfo(moduleFile.path);
const moduleInfo = service.getScriptInfo(moduleFile.path)!;
const sourceFile = moduleInfo.cacheSourceFile.sourceFile;
assert.equal(project.getSourceFile(moduleInfo.path), sourceFile);
+8 -8
View File
@@ -1447,7 +1447,7 @@ namespace ts.projectSystem {
commander: { typingLocation: commander.path, version: Semver.parse("1.3.0-next.0") }
});
const registry = createTypesRegistry("node", "commander");
registry.get("node")[`ts${versionMajorMinor}`] = "1.3.0-next.1";
registry.get("node")![`ts${versionMajorMinor}`] = "1.3.0-next.1";
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, ["http", "commander"], registry);
assert.deepEqual(logger.finish(), [
@@ -1535,8 +1535,8 @@ namespace ts.projectSystem {
content: "export let x: number"
};
const host = createServerHost([f1, packageFile, packageLockFile]);
let beginEvent: server.BeginInstallTypes;
let endEvent: server.EndInstallTypes;
let beginEvent!: server.BeginInstallTypes;
let endEvent!: server.EndInstallTypes;
const installer = new (class extends Installer {
constructor() {
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
@@ -1583,8 +1583,8 @@ namespace ts.projectSystem {
};
const cachePath = "/a/cache/";
const host = createServerHost([f1, packageFile]);
let beginEvent: server.BeginInstallTypes;
let endEvent: server.EndInstallTypes;
let beginEvent: server.BeginInstallTypes | undefined;
let endEvent: server.EndInstallTypes | undefined;
const installer: Installer = new (class extends Installer {
constructor() {
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
@@ -1611,8 +1611,8 @@ namespace ts.projectSystem {
assert.isTrue(!!beginEvent);
assert.isTrue(!!endEvent);
assert.isTrue(beginEvent.eventId === endEvent.eventId);
assert.isFalse(endEvent.installSuccess);
assert.isTrue(beginEvent!.eventId === endEvent!.eventId);
assert.isFalse(endEvent!.installSuccess);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path]);
});
@@ -1651,7 +1651,7 @@ namespace ts.projectSystem {
const appPath = "/a/b/app.js" as Path;
const foooPath = "/a/b/node_modules/fooo/index.d.ts";
function verifyResolvedModuleOfFooo(project: server.Project) {
const foooResolution = project.getLanguageService().getProgram().getSourceFileByPath(appPath).resolvedModules.get("fooo");
const foooResolution = project.getLanguageService().getProgram()!.getSourceFileByPath(appPath)!.resolvedModules!.get("fooo")!;
assert.equal(foooResolution.resolvedFileName, foooPath);
return foooResolution;
}
+16 -16
View File
@@ -46,7 +46,7 @@ var q:Point=<Point>p;`;
});
after(() => {
validateEditAtLineCharIndex = undefined;
validateEditAtLineCharIndex = undefined!;
});
it("handles empty lines array", () => {
@@ -105,10 +105,10 @@ and grew 1cm per day`;
});
after(() => {
validateEditAtPosition = undefined;
testContent = undefined;
lines = undefined;
lineMap = undefined;
validateEditAtPosition = undefined!;
testContent = undefined!;
lines = undefined!;
lineMap = undefined!;
});
it(`Insert at end of file`, () => {
@@ -201,7 +201,7 @@ and grew 1cm per day`;
before(() => {
// Use scanner.ts, decent size, does not change frequently
const testFileName = "src/compiler/scanner.ts";
testContent = Harness.IO.readFile(testFileName);
testContent = Harness.IO.readFile(testFileName)!;
const totalChars = testContent.length;
assert.isTrue(totalChars > 0, "Failed to read test file.");
@@ -237,16 +237,16 @@ and grew 1cm per day`;
});
after(() => {
rsa = undefined;
la = undefined;
las = undefined;
elas = undefined;
ersa = undefined;
ela = undefined;
lines = undefined;
lineMap = undefined;
lineIndex = undefined;
testContent = undefined;
rsa = undefined!;
la = undefined!;
las = undefined!;
elas = undefined!;
ersa = undefined!;
ela = undefined!;
lines = undefined!;
lineMap = undefined!;
lineIndex = undefined!;
testContent = undefined!;
});
it("Range (average length 1/4 file size)", () => {
+3 -3
View File
@@ -3,8 +3,8 @@
*/
namespace utils {
const testPathPrefixRegExp = /(?:(file:\/{3})|\/)\.(ts|lib|src)\//g;
export function removeTestPathPrefixes(text: string, retainTrailingDirectorySeparator?: boolean) {
return text !== undefined ? text.replace(testPathPrefixRegExp, (_, scheme) => scheme || (retainTrailingDirectorySeparator ? "/" : "")) : undefined;
export function removeTestPathPrefixes(text: string, retainTrailingDirectorySeparator?: boolean): string {
return text !== undefined ? text.replace(testPathPrefixRegExp, (_, scheme) => scheme || (retainTrailingDirectorySeparator ? "/" : "")) : undefined!; // TODO: GH#18217
}
/**
@@ -49,7 +49,7 @@ namespace utils {
}
function guessIndentation(lines: string[]) {
let indentation: number;
let indentation: number | undefined;
for (const line of lines) {
for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) {
if (!ts.isWhiteSpaceLike(line.charCodeAt(i))) {
+5 -5
View File
@@ -935,7 +935,7 @@ namespace vfs {
this._applyFilesWorker(value.files, path, deferred);
}
else {
deferred.push([value as Symlink | Link | Mount, path]);
deferred.push([value, path]);
}
}
}
@@ -998,7 +998,7 @@ namespace vfs {
directoryExists(path: string): boolean;
fileExists(path: string): boolean;
getFileSize(path: string): number;
readFile(path: string): string;
readFile(path: string): string | undefined;
getWorkspaceRoot(): string;
}
@@ -1020,7 +1020,7 @@ namespace vfs {
}
},
readFileSync(path: string): Buffer {
return Buffer.from(host.readFile(path), "utf8");
return Buffer.from(host.readFile(path)!, "utf8"); // TODO: GH#18217
}
};
}
@@ -1241,7 +1241,7 @@ namespace vfs {
ctimeMs: number; // status change time
birthtimeMs: number; // creation time
nlink: number; // number of hard links
symlink?: string;
symlink: string;
shadowRoot?: SymlinkInode;
meta?: collections.Metadata;
}
@@ -1262,7 +1262,7 @@ namespace vfs {
realpath: string;
basename: string;
parent: DirectoryInode | undefined;
links: collections.SortedMap<string, Inode> | undefined;
links: collections.SortedMap<string, Inode>;
node: Inode | undefined;
}
+17 -17
View File
@@ -179,7 +179,7 @@ interface Array<T> {}`
verifyMapSize(caption, actual, arrayFrom(expectedKeys.keys()));
expectedKeys.forEach((count, name) => {
assert.isTrue(actual.has(name), `${caption}: expected to contain ${name}, actual keys: ${arrayFrom(actual.keys())}`);
assert.equal(actual.get(name).length, count, `${caption}: Expected to be have ${count} entries for ${name}. Actual entry: ${JSON.stringify(actual.get(name))}`);
assert.equal(actual.get(name)!.length, count, `${caption}: Expected to be have ${count} entries for ${name}. Actual entry: ${JSON.stringify(actual.get(name))}`);
});
}
@@ -322,7 +322,7 @@ interface Array<T> {}`
readonly watchedFiles = createMultiMap<TestFileWatcher>();
private readonly executingFilePath: string;
private readonly currentDirectory: string;
private readonly dynamicPriorityWatchFile: HostWatchFile;
private readonly dynamicPriorityWatchFile: HostWatchFile | undefined;
private readonly customRecursiveWatchDirectory: HostWatchDirectory | undefined;
constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderorSymLinkList: ReadonlyArray<FileOrFolderOrSymLink>, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean, private readonly environmentVariables?: Map<string>) {
@@ -465,7 +465,7 @@ interface Array<T> {}`
else {
currentEntry.content = content;
currentEntry.modifiedTime = this.now();
this.fs.get(getDirectoryPath(currentEntry.path)).modifiedTime = this.now();
this.fs.get(getDirectoryPath(currentEntry.path))!.modifiedTime = this.now();
if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) {
this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath);
}
@@ -570,7 +570,7 @@ interface Array<T> {}`
this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath);
}
private removeFileOrFolder(fileOrDirectory: FsFile | FsFolder | FsSymLink, isRemovableLeafFolder: (folder: FsFolder) => boolean, isRenaming?: boolean) {
private removeFileOrFolder(fileOrDirectory: FsFile | FsFolder | FsSymLink, isRemovableLeafFolder: (folder: FsFolder) => boolean, isRenaming = false) {
const basePath = getDirectoryPath(fileOrDirectory.path);
const baseFolder = this.fs.get(basePath) as FsFolder;
if (basePath !== fileOrDirectory.path) {
@@ -621,15 +621,15 @@ interface Array<T> {}`
// For overriding the methods
invokeWatchedDirectoriesCallback(folderFullPath: string, relativePath: string) {
invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath));
invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath))!, cb => this.directoryCallback(cb, relativePath));
}
invokeWatchedDirectoriesRecursiveCallback(folderFullPath: string, relativePath: string) {
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath));
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(folderFullPath))!, cb => this.directoryCallback(cb, relativePath));
}
invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind, useFileNameInCallback?: boolean) {
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(fileFullPath)), ({ cb, fileName }) => cb(useFileNameInCallback ? fileName : fileFullPath, eventKind));
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(fileFullPath))!, ({ cb, fileName }) => cb(useFileNameInCallback ? fileName : fileFullPath, eventKind));
}
private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) {
@@ -687,11 +687,11 @@ interface Array<T> {}`
private toFsFolder(path: string): FsFolder {
const fsFolder = this.toFsEntry(path) as FsFolder;
fsFolder.entries = [] as SortedArray<FSEntry>;
fsFolder.entries = [] as FSEntry[] as SortedArray<FSEntry>; // https://github.com/Microsoft/TypeScript/issues/19873
return fsFolder;
}
private getRealFsEntry<T extends FSEntry>(isFsEntry: (fsEntry: FSEntry) => fsEntry is T, path: Path, fsEntry = this.fs.get(path)): T | undefined {
private getRealFsEntry<T extends FSEntry>(isFsEntry: (fsEntry: FSEntry) => fsEntry is T, path: Path, fsEntry = this.fs.get(path)!): T | undefined {
if (isFsEntry(fsEntry)) {
return fsEntry;
}
@@ -737,21 +737,21 @@ interface Array<T> {}`
getModifiedTime(s: string) {
const path = this.toFullPath(s);
const fsEntry = this.fs.get(path);
return fsEntry && fsEntry.modifiedTime;
return (fsEntry && fsEntry.modifiedTime)!; // TODO: GH#18217
}
readFile(s: string): string {
readFile(s: string): string | undefined {
const fsEntry = this.getRealFile(this.toFullPath(s));
return fsEntry ? fsEntry.content : undefined;
}
getFileSize(s: string) {
const path = this.toFullPath(s);
const entry = this.fs.get(path);
const entry = this.fs.get(path)!;
if (isFsFile(entry)) {
return entry.fileSize ? entry.fileSize : entry.content.length;
}
return undefined;
return undefined!; // TODO: GH#18217
}
directoryExists(s: string) {
@@ -812,7 +812,7 @@ interface Array<T> {}`
}
createSHA256Hash(s: string): string {
return sys.createSHA256Hash(s);
return sys.createSHA256Hash!(s);
}
watchFile(fileName: string, cb: FileWatcherCallback, pollingInterval: number) {
@@ -925,7 +925,7 @@ interface Array<T> {}`
}
const dirFullPath = this.realpath(getDirectoryPath(fullPath));
const realFullPath = combinePaths(dirFullPath, getBaseFileName(fullPath));
const fsEntry = this.fs.get(this.toPath(realFullPath));
const fsEntry = this.fs.get(this.toPath(realFullPath))!;
if (isFsSymLink(fsEntry)) {
return this.realpath(fsEntry.symLink);
}
@@ -934,7 +934,7 @@ interface Array<T> {}`
}
readonly exitMessage = "System Exit";
exitCode: number;
exitCode: number | undefined;
readonly resolvePath = (s: string) => s;
readonly getExecutingFilePath = () => this.executingFilePath;
readonly getCurrentDirectory = () => this.currentDirectory;
@@ -943,7 +943,7 @@ interface Array<T> {}`
throw new Error(this.exitMessage);
}
getEnvironmentVariable(name: string) {
return this.environmentVariables && this.environmentVariables.get(name);
return this.environmentVariables && this.environmentVariables.get(name) || "";
}
}
}