From 874518217cb98fa7439049372bb8995caaca0d12 Mon Sep 17 00:00:00 2001 From: andy-ms Date: Fri, 1 Sep 2017 15:22:39 -0700 Subject: [PATCH 1/3] Finish applying `no-object-literal-type-assertion` lint rule --- src/compiler/checker.ts | 19 +- src/compiler/commandLineParser.ts | 5 +- src/harness/unittests/session.ts | 6 +- .../unittests/tsserverProjectSystem.ts | 177 +++++++++--------- src/server/editorServices.ts | 1 + src/server/project.ts | 2 + src/server/session.ts | 6 +- .../typingsInstaller/typingsInstaller.ts | 9 +- tslint.json | 1 + 9 files changed, 125 insertions(+), 101 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 269666d7b9468..ddb80cb2d54a6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1862,8 +1862,10 @@ namespace ts { target.set(id, sourceSymbol); if (lookupTable && exportNode) { lookupTable.set(id, { - specifierText: getTextOfNode(exportNode.moduleSpecifier) - } as ExportCollisionTracker); + specifierText: getTextOfNode(exportNode.moduleSpecifier), + // TODO: GH#18217: (exportsWithDuplicate was not present) + exportsWithDuplicate: undefined, + }); } } else if (lookupTable && exportNode && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) { @@ -6298,18 +6300,21 @@ namespace ts { function createTypePredicateFromTypePredicateNode(node: TypePredicateNode): IdentifierTypePredicate | ThisTypePredicate { const { parameterName } = node; if (parameterName.kind === SyntaxKind.Identifier) { - return { + const result: IdentifierTypePredicate = { kind: TypePredicateKind.Identifier, - parameterName: parameterName ? parameterName.escapedText : undefined, + // TODO: GH#18217 (should unescape parameterName) + parameterName: parameterName ? parameterName.escapedText as string : undefined, parameterIndex: parameterName ? getTypePredicateParameterIndex((node.parent as SignatureDeclaration).parameters, parameterName) : undefined, - type: getTypeFromTypeNode(node.type) - } as IdentifierTypePredicate; + type: getTypeFromTypeNode(node.type), + }; + return result; } else { - return { + const result: ThisTypePredicate = { kind: TypePredicateKind.This, type: getTypeFromTypeNode(node.type) }; + return result; } } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index c92d147f9a93a..6ff472ca4b763 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -907,7 +907,10 @@ namespace ts { */ export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): JsonSourceFile { const textOrDiagnostic = tryReadFile(fileName, readFile); - return typeof textOrDiagnostic === "string" ? parseJsonText(fileName, textOrDiagnostic) : { parseDiagnostics: [textOrDiagnostic] }; + return typeof textOrDiagnostic === "string" + ? parseJsonText(fileName, textOrDiagnostic) + // tslint:disable-next-line no-object-literal-type-assertion (TODO:GH#18217) + : { parseDiagnostics: [textOrDiagnostic] }; } function tryReadFile(fileName: string, readFile: (path: string) => string | undefined): string | Diagnostic { diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 18109cfa9db26..80ec6fb414f4d 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -148,9 +148,9 @@ namespace ts.server { } }; session.onMessage(JSON.stringify(setOptionsRequest)); - assert.deepEqual( + assert.deepEqual( session.getProjectService().getCompilerOptionsForInferredProjects(), - { + { module: ModuleKind.System, target: ScriptTarget.ES5, jsx: JsxEmit.React, @@ -284,7 +284,7 @@ namespace ts.server { session.onMessage(JSON.stringify(req)); - expect(lastSent).to.deep.equal({ + assert.deepEqual(lastSent as protocol.ConfigureResponse, { command: CommandNames.Configure, type: "response", success: true, diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index c9a7431097734..665fb954fce29 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -710,7 +710,7 @@ namespace ts.projectSystem { const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false }); const service = createProjectService(host); - service.openExternalProject({ + service.openExternalProject({ projectFileName: "/a/b/project.csproj", rootFiles: toExternalFiles([f1.path, combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path))]), options: {} @@ -867,10 +867,10 @@ namespace ts.projectSystem { const host = createServerHost([f1, f2, f3]); const session = createSession(host); - session.executeCommand({ + session.executeCommand({ seq: 1, type: "request", - command: "openExternalProjects", + command: protocol.CommandTypes.OpenExternalProjects, arguments: { projects: [p1, p2] } }); @@ -879,28 +879,28 @@ namespace ts.projectSystem { assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName); assert.equal(projectService.externalProjects[1].getProjectName(), p2.projectFileName); - session.executeCommand({ + session.executeCommand({ seq: 2, type: "request", - command: "openExternalProjects", + command: protocol.CommandTypes.OpenExternalProjects, arguments: { projects: [p1, p3] } }); checkNumberOfProjects(projectService, { externalProjects: 2 }); assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName); assert.equal(projectService.externalProjects[1].getProjectName(), p3.projectFileName); - session.executeCommand({ + session.executeCommand({ seq: 3, type: "request", - command: "openExternalProjects", + command: protocol.CommandTypes.OpenExternalProjects, arguments: { projects: [] } }); checkNumberOfProjects(projectService, { externalProjects: 0 }); - session.executeCommand({ + session.executeCommand({ seq: 3, type: "request", - command: "openExternalProjects", + command: protocol.CommandTypes.OpenExternalProjects, arguments: { projects: [p2] } }); assert.equal(projectService.externalProjects[0].getProjectName(), p2.projectFileName); @@ -2290,10 +2290,10 @@ namespace ts.projectSystem { lastEvent = e; } }); - session.executeCommand({ + session.executeCommand({ seq: 0, type: "request", - command: "open", + command: protocol.CommandTypes.Open, arguments: { file: f1.path } }); const projectService = session.getProjectService(); @@ -2342,10 +2342,10 @@ namespace ts.projectSystem { lastEvent = e; } }); - session.executeCommand({ + session.executeCommand({ seq: 0, type: "request", - command: "open", + command: protocol.CommandTypes.Open, arguments: { file: f1.path } }); @@ -3501,17 +3501,17 @@ namespace ts.projectSystem { const session = createSession(host); // send open request - session.executeCommand({ + session.executeCommand({ type: "request", - command: "open", + command: protocol.CommandTypes.Open, seq: 1, arguments: { file: f1.path } }); // reload from tmp file - session.executeCommand({ + session.executeCommand({ type: "request", - command: "reload", + command: protocol.CommandTypes.Reload, seq: 2, arguments: { file: f1.path, tmpfile: tmp.path } }); @@ -3521,12 +3521,13 @@ namespace ts.projectSystem { const snap1 = projectServiice.getScriptInfo(f1.path).getSnapshot(); assert.equal(snap1.getText(0, snap1.getLength()), tmp.content, "content should be equal to the content of temp file"); - // reload from original file file - session.executeCommand({ + // reload from original file + session.executeCommand({ type: "request", - command: "reload", + command: protocol.CommandTypes.Reload, seq: 2, - arguments: { file: f1.path } + // TODO: GH#18217 (tmpfile was not present) + arguments: { file: f1.path, tmpfile: undefined } }); // verify content @@ -3544,20 +3545,20 @@ namespace ts.projectSystem { }; const host = createServerHost([f]); const session = createSession(host); - session.executeCommand({ + session.executeCommand({ seq: 1, type: "request", - command: "compilerOptionsForInferredProjects", + command: protocol.CommandTypes.CompilerOptionsForInferredProjects, arguments: { options: { allowJs: true } } }); - session.executeCommand({ + session.executeCommand({ seq: 2, type: "request", - command: "open", + command: protocol.CommandTypes.Open, arguments: { file: f.path, fileContent: f.content, @@ -3579,7 +3580,7 @@ namespace ts.projectSystem { useSingleInferredProject: true, useInferredProjectPerProjectRoot: true }); - session.executeCommand({ + session.executeCommand({ seq: 1, type: "request", command: CommandNames.CompilerOptionsForInferredProjects, @@ -3590,7 +3591,7 @@ namespace ts.projectSystem { } } }); - session.executeCommand({ + session.executeCommand({ seq: 2, type: "request", command: CommandNames.CompilerOptionsForInferredProjects, @@ -3602,7 +3603,7 @@ namespace ts.projectSystem { projectRootPath: "/b" } }); - session.executeCommand({ + session.executeCommand({ seq: 3, type: "request", command: CommandNames.Open, @@ -3613,7 +3614,7 @@ namespace ts.projectSystem { projectRootPath: file1.projectRootPath } }); - session.executeCommand({ + session.executeCommand({ seq: 4, type: "request", command: CommandNames.Open, @@ -3624,7 +3625,7 @@ namespace ts.projectSystem { projectRootPath: file2.projectRootPath } }); - session.executeCommand({ + session.executeCommand({ seq: 5, type: "request", command: CommandNames.Open, @@ -3635,7 +3636,7 @@ namespace ts.projectSystem { projectRootPath: file3.projectRootPath } }); - session.executeCommand({ + session.executeCommand({ seq: 6, type: "request", command: CommandNames.Open, @@ -3671,7 +3672,7 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { inferredProjects: 1 }); const projectName = projectService.inferredProjects[0].getProjectName(); - const diags = session.executeCommand({ + const diags = session.executeCommand({ type: "request", command: server.CommandNames.CompilerOptionsDiagnosticsFull, seq: 2, @@ -3679,13 +3680,13 @@ namespace ts.projectSystem { }).response; assert.isTrue(diags.length === 0); - session.executeCommand({ + session.executeCommand({ type: "request", command: server.CommandNames.CompilerOptionsForInferredProjects, seq: 3, arguments: { options: { module: ModuleKind.CommonJS } } }); - const diagsAfterUpdate = session.executeCommand({ + const diagsAfterUpdate = session.executeCommand({ type: "request", command: server.CommandNames.CompilerOptionsDiagnosticsFull, seq: 4, @@ -3704,7 +3705,7 @@ namespace ts.projectSystem { const projectService = session.getProjectService(); const projectFileName = "/a/b/project.csproj"; const externalFiles = toExternalFiles([f1.path]); - projectService.openExternalProject({ + projectService.openExternalProject({ projectFileName, rootFiles: externalFiles, options: {} @@ -3712,7 +3713,7 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { externalProjects: 1 }); - const diags = session.executeCommand({ + const diags = session.executeCommand({ type: "request", command: server.CommandNames.CompilerOptionsDiagnosticsFull, seq: 2, @@ -3720,7 +3721,7 @@ namespace ts.projectSystem { }).response; assert.isTrue(diags.length === 0); - session.executeCommand({ + session.executeCommand({ type: "request", command: server.CommandNames.OpenExternalProject, seq: 3, @@ -3730,7 +3731,7 @@ namespace ts.projectSystem { options: { module: ModuleKind.CommonJS } } }); - const diagsAfterUpdate = session.executeCommand({ + const diagsAfterUpdate = session.executeCommand({ type: "request", command: server.CommandNames.CompilerOptionsDiagnosticsFull, seq: 4, @@ -3759,17 +3760,17 @@ namespace ts.projectSystem { }; const host = createServerHost([f1, f2, config]); const session = createSession(host); - session.executeCommand({ + session.executeCommand({ seq: 1, type: "request", - command: "open", + command: protocol.CommandTypes.Open, arguments: { file: f1.path } }); checkNumberOfProjects(session.getProjectService(), { configuredProjects: 1 }); - const { response } = session.executeCommand({ + const { response } = session.executeCommand({ seq: 2, type: "request", - command: "compileOnSaveAffectedFileList", + command: protocol.CommandTypes.CompileOnSaveAffectedFileList, arguments: { file: f1.path } }); assert.equal((response).length, 1, "expected output for 1 project"); @@ -3851,20 +3852,21 @@ namespace ts.projectSystem { const session = createSession(host, { cancellationToken }); expectedRequestId = session.getNextSeq(); - session.executeCommandSeq({ - command: "open", + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, arguments: { file: f1.path } }); expectedRequestId = session.getNextSeq(); - session.executeCommandSeq({ - command: "geterr", - arguments: { files: [f1.path] } + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + // TODO: GH#18217 (delay was not present) + arguments: { files: [f1.path], delay: undefined } }); expectedRequestId = session.getNextSeq(); - session.executeCommandSeq({ - command: "occurrences", + session.executeCommandSeq({ + command: protocol.CommandTypes.Occurrences, arguments: { file: f1.path, line: 1, offset: 6 } }); @@ -3894,14 +3896,15 @@ namespace ts.projectSystem { cancellationToken }); { - session.executeCommandSeq({ - command: "open", + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, arguments: { file: f1.path } }); // send geterr for missing file - session.executeCommandSeq({ - command: "geterr", - arguments: { files: ["/a/missing"] } + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + // TODO: GH#18217 (delay was not present) + arguments: { files: ["/a/missing"], delay: undefined } }); // no files - expect 'completed' event assert.equal(host.getOutput().length, 1, "expect 1 message"); @@ -3910,17 +3913,19 @@ namespace ts.projectSystem { { const getErrId = session.getNextSeq(); // send geterr for a valid file - session.executeCommandSeq({ - command: "geterr", - arguments: { files: [f1.path] } + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + // TODO: GH#18217 (delay was not present) + arguments: { files: [f1.path], delay: undefined } }); assert.equal(host.getOutput().length, 0, "expect 0 messages"); // run new request - session.executeCommandSeq({ - command: "projectInfo", - arguments: { file: f1.path } + session.executeCommandSeq({ + command: protocol.CommandTypes.ProjectInfo, + // TODO: GH#18217 (needFileNameList was not present) + arguments: { file: f1.path, needFileNameList: undefined } }); host.clearOutput(); @@ -3935,9 +3940,10 @@ namespace ts.projectSystem { } { const getErrId = session.getNextSeq(); - session.executeCommandSeq({ - command: "geterr", - arguments: { files: [f1.path] } + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + // TODO: GH#18217 (delay was not present) + arguments: { files: [f1.path], delay: undefined } }); assert.equal(host.getOutput().length, 0, "expect 0 messages"); @@ -3957,9 +3963,10 @@ namespace ts.projectSystem { } { const getErrId = session.getNextSeq(); - session.executeCommandSeq({ - command: "geterr", - arguments: { files: [f1.path] } + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + // TODO: GH#18217 (delay was not present) + arguments: { files: [f1.path], delay: undefined } }); assert.equal(host.getOutput().length, 0, "expect 0 messages"); @@ -3981,9 +3988,10 @@ namespace ts.projectSystem { } { const getErr1 = session.getNextSeq(); - session.executeCommandSeq({ - command: "geterr", - arguments: { files: [f1.path] } + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + // TODO: GH#18217 (delay was not present) + arguments: { files: [f1.path], delay: undefined } }); assert.equal(host.getOutput().length, 0, "expect 0 messages"); // run first step @@ -3993,9 +4001,10 @@ namespace ts.projectSystem { assert.equal(e1.event, "syntaxDiag"); host.clearOutput(); - session.executeCommandSeq({ - command: "geterr", - arguments: { files: [f1.path] } + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + // TODO: GH#18217 (delay was not present) + arguments: { files: [f1.path], delay: undefined } }); // make sure that getErr1 is completed verifyRequestCompleted(getErr1, 0); @@ -4032,32 +4041,32 @@ namespace ts.projectSystem { throttleWaitMilliseconds: 0 }); { - session.executeCommandSeq({ - command: "open", + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, arguments: { file: f1.path } }); // send navbar request (normal priority) - session.executeCommandSeq({ - command: "navbar", + session.executeCommandSeq({ + command: protocol.CommandTypes.NavBar, arguments: { file: f1.path } }); // ensure the nav bar request can be canceled - verifyExecuteCommandSeqIsCancellable({ - command: "navbar", + verifyExecuteCommandSeqIsCancellable({ + command: protocol.CommandTypes.NavBar, arguments: { file: f1.path } }); // send outlining spans request (normal priority) - session.executeCommandSeq({ - command: "outliningSpans", + session.executeCommandSeq({ + command: protocol.CommandTypes.OutliningSpans, arguments: { file: f1.path } }); // ensure the outlining spans request can be canceled - verifyExecuteCommandSeqIsCancellable({ - command: "outliningSpans", + verifyExecuteCommandSeqIsCancellable({ + command: protocol.CommandTypes.OutliningSpans, arguments: { file: f1.path } }); } @@ -4211,7 +4220,7 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { configuredProjects: 1 }); const projectName = projectService.configuredProjects[0].getProjectName(); - const diags = session.executeCommand({ + const diags = session.executeCommand({ type: "request", command: server.CommandNames.SemanticDiagnosticsSync, seq: 2, @@ -4223,7 +4232,7 @@ namespace ts.projectSystem { host.reloadFS([file, configFile]); host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Changed); - const diagsAfterEdit = session.executeCommand({ + const diagsAfterEdit = session.executeCommand({ type: "request", command: server.CommandNames.SemanticDiagnosticsSync, seq: 2, diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index d849226a35b8d..1f70ee9df6506 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1039,6 +1039,7 @@ namespace ts.server { const result = parseJsonText(configFilename, configFileContent); if (!result.endOfFileToken) { + // tslint:disable-next-line no-object-literal-type-assertion (TODO: GH#18217) result.endOfFileToken = { kind: SyntaxKind.EndOfFileToken }; } const errors = result.parseDiagnostics; diff --git a/src/server/project.ts b/src/server/project.ts index 623c43e8d3ace..94ac849f492fa 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1003,6 +1003,8 @@ namespace ts.server { if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue; // Provide global: true so plugins can detect why they can't find their config + // TODO: Pluginimport has no `global` property! + // tslint:disable-next-line no-object-literal-type-assertion (TODO: GH#18217) this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths); } } diff --git a/src/server/session.ts b/src/server/session.ts index 0b3038a408d87..b6bb84349d49a 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -568,7 +568,7 @@ namespace ts.server { } private convertToDiagnosticsWithLinePosition(diagnostics: ReadonlyArray, scriptInfo: ScriptInfo): protocol.DiagnosticWithLinePosition[] { - return diagnostics.map(d => { + return diagnostics.map(d => ({ message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), start: d.start, length: d.length, @@ -577,7 +577,7 @@ namespace ts.server { source: d.source, startLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start), endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start + d.length) - }); + })); } private getDiagnosticsWorker( @@ -1952,7 +1952,7 @@ namespace ts.server { } } - public executeCommand(request: protocol.Request): { response?: any, responseRequired?: boolean } { + public executeCommand(request: T): { response?: any, responseRequired?: boolean } { const handler = this.handlers.get(request.command); if (handler) { return this.executeWithRequestId(request.seq, () => handler(request)); diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index c6423bc3c7b50..575122e433b87 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -328,12 +328,15 @@ namespace ts.server.typingsInstaller { this.installRunCount++; // send progress event - this.sendResponse({ + const response: BeginInstallTypes = { kind: EventBeginInstallTypes, eventId: requestId, typingsInstallerVersion: ts.version, // qualified explicitly to prevent occasional shadowing - projectName: req.projectName - }); + projectName: req.projectName, + // TODO: GH#18217 (property was not present) + packagesToInstall: undefined + }; + this.sendResponse(response); const scopedTypings = filteredTypings.map(typingsName); this.installTypingsAsync(requestId, scopedTypings, cachePath, ok => { diff --git a/tslint.json b/tslint.json index de60ad7683aa0..5af1a444e2a8c 100644 --- a/tslint.json +++ b/tslint.json @@ -23,6 +23,7 @@ "no-inferrable-types": true, "no-internal-module": true, "no-null-keyword": true, + "no-object-literal-type-assertion": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": [true, "ignore-template-strings"], "no-type-assertion-whitespace": true, From 6ccb5e7c4c395f96a643307293431ba538c8173f Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 1 Nov 2017 08:54:05 -0700 Subject: [PATCH 2/3] fix --- src/server/editorServices.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index d67290839c265..d046ebee5982c 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1601,10 +1601,11 @@ namespace ts.server { return; } - this.eventHandler({ + const event: ConfigFileDiagEvent = { eventName: ConfigFileDiagEvent, - data: { configFileName: project.getConfigFilePath(), diagnostics: project.getAllProjectErrors(), triggerFile } - }); + data: { configFileName: project.getConfigFilePath(), diagnostics: project.getAllProjectErrors(), triggerFile }, + }; + this.eventHandler(event); } private getOrCreateInferredProjectForProjectRootPathIfEnabled(info: ScriptInfo, projectRootPath: NormalizedPath | undefined): InferredProject | undefined { From 369966351e45b4869971dd2f37adf65f8b0872eb Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 3 Nov 2017 10:52:36 -0700 Subject: [PATCH 3/3] Update baseline --- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 26c03954b4544..e163bc440d5e1 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7000,7 +7000,7 @@ declare namespace ts.server { private setCurrentRequest(requestId); private resetCurrentRequest(requestId); executeWithRequestId(requestId: number, f: () => T): T; - executeCommand(request: protocol.Request): HandlerResponse; + executeCommand(request: T): HandlerResponse; onMessage(message: string): void; } interface HandlerResponse {