diff --git a/.eslintrc.json b/.eslintrc.json index 1f39c29101f83..b10c839600b95 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -49,6 +49,7 @@ "allowDeclarations": true }], "no-double-space": "error", + "async-type": "error", "boolean-trivia": "error", "no-in-operator": "error", "simple-indent": "error", diff --git a/scripts/eslint/rules/async-type.ts b/scripts/eslint/rules/async-type.ts new file mode 100644 index 0000000000000..7e619445bbe4a --- /dev/null +++ b/scripts/eslint/rules/async-type.ts @@ -0,0 +1,40 @@ +import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/experimental-utils"; +import { createRule } from "./utils"; + +export = createRule({ + name: "async-type", + meta: { + docs: { + description: ``, + category: "Possible Errors", + recommended: "error", + }, + messages: { + asyncTypeError: `Async functions must have an explicit return type`, + }, + schema: [], + type: "problem", + }, + defaultOptions: [], + + create(context) { + const checkAsyncFunction = (node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression) => { + if (node.async && !node.returnType) { + context.report({ messageId: "asyncTypeError", node }); + } + }; + + const checkAsyncMethod = (node: TSESTree.MethodDefinition) => { + if (node.value.type === AST_NODE_TYPES.FunctionExpression) { + checkAsyncFunction(node.value); + } + }; + + return { + [AST_NODE_TYPES.FunctionDeclaration]: checkAsyncFunction, + [AST_NODE_TYPES.FunctionExpression]: checkAsyncFunction, + [AST_NODE_TYPES.ArrowFunctionExpression]: checkAsyncFunction, + [AST_NODE_TYPES.MethodDefinition]: checkAsyncMethod + }; + }, +}); diff --git a/scripts/eslint/tests/async-type.test.ts b/scripts/eslint/tests/async-type.test.ts new file mode 100644 index 0000000000000..b80bbae7b4452 --- /dev/null +++ b/scripts/eslint/tests/async-type.test.ts @@ -0,0 +1,55 @@ +import { RuleTester } from "./support/RuleTester"; +import rule = require("../rules/async-type"); + +const ruleTester = new RuleTester({ + parserOptions: { + warnOnUnsupportedTypeScriptVersion: false, + }, + parser: require.resolve("@typescript-eslint/parser"), +}); + +ruleTester.run("async-type", rule, { + valid: [ + { + code: ` +async function foo(): Promise {} + `, + }, + { + code: ` +const fn = async function(): Promise {} + `, + }, + { + code: ` +const fn = async (): Promise => {} + `, + }, + { + code: ` +class C { + async method(): Promise {} +} + `, + }, + ], + + invalid: [ + { + code: `async function foo() {}`, + errors: [{ messageId: "asyncTypeError" }] + }, + { + code: `const fn = async function() {}`, + errors: [{ messageId: "asyncTypeError" }] + }, + { + code: `const fn = async () => {}`, + errors: [{ messageId: "asyncTypeError" }] + }, + { + code: `class C { async method() {} }`, + errors: [{ messageId: "asyncTypeError" }] + }, + ] +}); \ No newline at end of file diff --git a/scripts/open-cherry-pick-pr.ts b/scripts/open-cherry-pick-pr.ts index ac39d959bd973..e8ae3c9a44c5f 100644 --- a/scripts/open-cherry-pick-pr.ts +++ b/scripts/open-cherry-pick-pr.ts @@ -1,7 +1,9 @@ +/* eslint-disable async-type */ /// // Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally /// + import Octokit = require("@octokit/rest"); const {runSequence} = require("./run-sequence"); import fs = require("fs"); diff --git a/scripts/open-user-pr.ts b/scripts/open-user-pr.ts index a2472f3a9fab7..2b1597f736c60 100644 --- a/scripts/open-user-pr.ts +++ b/scripts/open-user-pr.ts @@ -1,5 +1,7 @@ +/* eslint-disable async-type */ /// /// + // Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally import Octokit = require("@octokit/rest"); import {runSequence} from "./run-sequence"; diff --git a/scripts/produceLKG.ts b/scripts/produceLKG.ts index 762290bcfa6a0..f4da4e8fc6fdb 100644 --- a/scripts/produceLKG.ts +++ b/scripts/produceLKG.ts @@ -1,3 +1,4 @@ +/* eslint-disable async-type */ /// import childProcess = require("child_process"); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 4941257cabf64..e350c01d0d20a 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -58,6 +58,23 @@ namespace ts { push(...values: T[]): void; } + /* @internal */ + export interface PromiseConstructor extends PromiseConstructorLike { + new (executor: (resolve: (value: T | PromiseLike) => void, reject: (reason: unknown) => void) => void): Promise; + prototype: Promise; + resolve(value: T | PromiseLike): Promise; + resolve(): Promise; + reject(reason: unknown): Promise; + all(promises: (T | PromiseLike)[]): Promise; + race(promises: (T | PromiseLike)[]): Promise; + } + + /* @internal */ + export interface Promise { + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | undefined | null): Promise; + catch(onrejected?: ((reason: unknown) => TResult | PromiseLike) | undefined | null): Promise; + } + /* @internal */ export type EqualityComparer = (a: T, b: T) => boolean; @@ -75,8 +92,9 @@ namespace ts { /* @internal */ namespace ts { // Natives - // NOTE: This must be declared in a separate block from the one below so that we don't collide with the exported definition of `Map`. - declare const Map: (new () => Map) | undefined; + // NOTE: These must be declared in a separate block from the one below so that we don't collide with the exported definitions of `Map` and `Promise`. + declare const Map: MapConstructor | undefined; + declare const Promise: PromiseConstructor | undefined; /** * Returns the native Map implementation if it is available and compatible (i.e. supports iteration). @@ -86,6 +104,10 @@ namespace ts { // eslint-disable-next-line no-in-operator return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined; } + + export function tryGetNativePromise(): PromiseConstructor | undefined { + return typeof Promise === "function" ? Promise : undefined; + } } /* @internal */ @@ -100,6 +122,14 @@ namespace ts { throw new Error("TypeScript requires an environment that provides a compatible native Map implementation."); })(); + export const Promise: PromiseConstructor = tryGetNativePromise() || (() => { + // NOTE: createPromiseShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createPromiseShim === "function") { + return createPromiseShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native Promise implementation."); + })(); + /** Create a new map. */ export function createMap(): Map { return new Map(); diff --git a/src/shims/promiseShim.ts b/src/shims/promiseShim.ts new file mode 100644 index 0000000000000..448d0dc331528 --- /dev/null +++ b/src/shims/promiseShim.ts @@ -0,0 +1,172 @@ +/* @internal */ +namespace ts { + declare function setTimeout(handler: (...args: A) => void, timeout: number, ...args: A): any; + + export interface Promise extends globalThis.Promise { + } + + export interface PromiseConstructor extends PromiseConstructorLike { + new (executor: (resolve: (value: T | PromiseLike) => void, reject: (reason: unknown) => void) => void): Promise; + resolve(value: T | PromiseLike): Promise; + resolve(): Promise; + reject(reason: unknown): Promise; + all(promises: (T | PromiseLike)[]): Promise; + race(promises: (T | PromiseLike)[]): Promise; + } + + export function createPromiseShim(): PromiseConstructor { + class Promise implements ts.Promise { + private _state: "pending" | "fulfilled" | "rejected" = "pending"; + private _result: unknown; + private _reactions: PromiseReaction[] = []; + + constructor(executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason: any) => void) => void) { + const { resolve, reject } = createResolvingFunctions(this); + try { + executor(resolve, reject); + } + catch (e) { + reject(e); + } + } + + then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null + ): Promise { + return new Promise((resolve, reject) => { + const reaction: PromiseReaction = { resolve, reject, onfulfilled, onrejected }; + if (this._state === "pending") { + this._reactions.push(reaction); + } + else { + setTimeout(promiseReactionJob, 0, reaction, this._state === "fulfilled" ? "fulfill" : "reject", this._result); + } + }); + } + + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise { + return this.then(/*onfulfilled*/ undefined, onrejected); + } + + static resolve(): Promise; + static resolve(value: T | PromiseLike): Promise; + static resolve(value?: T | PromiseLike): Promise { + return value instanceof this && value.constructor === this ? value : new Promise(resolve => resolve(value)); + } + + static reject(reason: any): Promise { + return new Promise((_, reject) => reject(reason)); + } + + static all(promises: (T | PromiseLike)[]): Promise { + return new Promise((resolve, reject) => { + let count = promises.length; + const values: T[] = Array(count); + for (let i = 0; i < promises.length; i++) { + let called = false; + this.resolve(promises[i]).then( + value => { + if (!called) { + called = true; + values[i] = value; + count--; + if (count === 0) { + resolve(values); + } + } + }, + reject); + } + }); + } + + static race(promises: (T | PromiseLike)[]): Promise { + return new Promise((resolve, reject) => { + for (const promise of promises) { + this.resolve(promise).then(resolve, reject); + } + }); + } + } + + interface PromiseReaction { + resolve: (value: unknown) => void; + reject: (reason: unknown) => void; + onfulfilled?: ((value: unknown) => unknown) | null; + onrejected?: ((reason: unknown) => unknown) | null; + } + + function createResolvingFunctions(promise: Promise) { + let called = false; + return { + resolve: (value: T | Promise) => { + if (!called) { + called = true; + try { + if (promise === value) throw new TypeError(); + // eslint-disable-next-line no-null/no-null + const then = typeof value === "object" && value !== null && (>value).then; + if (typeof then !== "function") { + settlePromise(promise, "fulfill", value); + } + else { + setTimeout(resolveThenableJob, 0, promise, value, then); + } + } + catch (e) { + settlePromise(promise, "reject", e); + } + } + }, + reject: (reason: any) => { + if (!called) { + called = true; + settlePromise(promise, "reject", reason); + } + } + }; + } + + function settlePromise(promise: Promise, verb: "fulfill" | "reject", value: unknown) { + /* eslint-disable dot-notation */ + const reactions = promise["_reactions"]; + promise["_result"] = value; + promise["_reactions"] = undefined!; + promise["_state"] = verb === "fulfill" ? "fulfilled" : "rejected"; + for (const reaction of reactions) { + setTimeout(promiseReactionJob, 0, reaction, verb, value); + } + /* eslint-enable dot-notation */ + } + + function resolveThenableJob(promiseToResolve: Promise, thenable: T, thenAction: Promise["then"]) { + const { resolve, reject } = createResolvingFunctions(promiseToResolve); + try { + thenAction.call(thenable, resolve, reject); + } + catch (e) { + reject(e); + } + } + + function promiseReactionJob(reaction: PromiseReaction, verb: "fulfill" | "reject", argument: unknown) { + const handler = verb === "fulfill" ? reaction.onfulfilled : reaction.onrejected; + if (handler) { + try { + argument = handler(argument); + verb = "fulfill"; + } + catch (e) { + argument = e; + verb = "reject"; + } + } + + const action = verb === "fulfill" ? reaction.resolve : reaction.reject; + action(argument); + } + + return Promise; + } +} \ No newline at end of file diff --git a/src/shims/tsconfig.json b/src/shims/tsconfig.json index cb058762fd616..d433975cf9d3d 100644 --- a/src/shims/tsconfig.json +++ b/src/shims/tsconfig.json @@ -4,6 +4,7 @@ "outFile": "../../built/local/shims.js" }, "files": [ - "mapShim.ts" + "mapShim.ts", + "promiseShim.ts" ] } diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 27a7712a2b14f..027154abd65cf 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -62,6 +62,7 @@ "unittests/reuseProgramStructure.ts", "unittests/semver.ts", "unittests/createMapShim.ts", + "unittests/createPromiseShim.ts", "unittests/transform.ts", "unittests/config/commandLineParsing.ts", "unittests/config/configurationExtension.ts", diff --git a/src/testRunner/unittests/createPromiseShim.ts b/src/testRunner/unittests/createPromiseShim.ts new file mode 100644 index 0000000000000..34b84b61f890b --- /dev/null +++ b/src/testRunner/unittests/createPromiseShim.ts @@ -0,0 +1,131 @@ +describe("unittests:: createPromiseShim", () => { + function expectFulfilled(done: Mocha.Done, value: T) { + return (_value: T) => { + try { + assert.strictEqual(_value, value); + } + catch (e) { + return done(e); + } + done(); + }; + } + + function expectNotFulfilled(done: Mocha.Done) { + return () => done(new Error("expected promise to be rejected")); + } + + function expectNotRejected(done: Mocha.Done) { + return (_reason: any) => done(_reason); + } + + function expectRejected(done: Mocha.Done, reason: any) { + return (_reason: any) => { + try { + assert.strictEqual(_reason, reason); + } + catch (e) { + return done(e); + } + done(); + }; + } + + it("executor resolves correctly with non-promise", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = {}; + new PromiseShim(resolve => resolve(value)).then( + expectFulfilled(done, value), + expectNotRejected(done) + ); + }).timeout(500); + + it("executor resolves correctly with promise", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = { }; + const p = { then(cb: (v: {}) => void) { cb(value); } }; + new PromiseShim(resolve => resolve(p)).then( + expectFulfilled(done, value), + expectNotRejected(done) + ); + }).timeout(500); + + it("executor rejects correctly", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = { }; + new PromiseShim((_, reject) => reject(value)).then( + expectNotFulfilled(done), + expectRejected(done, value) + ); + }).timeout(500); + + it("catches error in executor", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = {}; + new PromiseShim(() => { throw value; }).then( + expectNotFulfilled(done), + expectRejected(done, value) + ); + }).timeout(500); + + it("catches error in onfulfilled of 'then'", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = {}; + new PromiseShim(resolve => resolve(/*value*/ undefined)).then( + () => { throw value; }, + expectNotRejected(done) + ).then( + expectNotFulfilled(done), + expectRejected(done, value) + ); + }).timeout(500); + + it("catches error in onrejected of 'then'", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const first = {}; + const second = {}; + new PromiseShim((_, reject) => reject(first)).then( + expectNotFulfilled(done), + _ => { throw second; } + ).then( + expectNotFulfilled(done), + expectRejected(done, second) + ); + }).timeout(500); + + it("Promise.resolve resolves correctly with non-promise", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = {}; + PromiseShim.resolve(value).then( + expectFulfilled(done, value), + expectNotRejected(done) + ); + }).timeout(500); + + it("Promsie.resolve resolves correctly with promise", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = { }; + const p = { then(cb: (v: {}) => void) { cb(value); } }; + PromiseShim.resolve(p).then( + expectFulfilled(done, value), + expectNotRejected(done) + ); + }).timeout(500); + + it("Promsie.resolve returns same promise if instance", () => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = { }; + const first = PromiseShim.resolve(value); + const second = PromiseShim.resolve(first); + assert.strictEqual(first, second); + }); + + it("Promise.reject rejects correctly", done => { + const PromiseShim = ts.createPromiseShim(); // tslint:disable-line variable-name + const value = { }; + PromiseShim.reject(value).then( + expectNotFulfilled(done), + expectRejected(done, value) + ); + }).timeout(500); +}); \ No newline at end of file diff --git a/src/testRunner/unittests/evaluation/asyncArrow.ts b/src/testRunner/unittests/evaluation/asyncArrow.ts index 04a7af613fa38..0effd59e08274 100644 --- a/src/testRunner/unittests/evaluation/asyncArrow.ts +++ b/src/testRunner/unittests/evaluation/asyncArrow.ts @@ -1,6 +1,6 @@ describe("unittests:: evaluation:: asyncArrowEvaluation", () => { // https://github.com/Microsoft/TypeScript/issues/24722 - it("this capture (es5)", async () => { + it("this capture (es5)", async (): Promise => { const result = evaluator.evaluateTypeScript(` export class A { b = async (...args: any[]) => { diff --git a/src/testRunner/unittests/evaluation/asyncGenerator.ts b/src/testRunner/unittests/evaluation/asyncGenerator.ts index 8ca531f0759a1..afdefec243c4f 100644 --- a/src/testRunner/unittests/evaluation/asyncGenerator.ts +++ b/src/testRunner/unittests/evaluation/asyncGenerator.ts @@ -1,5 +1,5 @@ describe("unittests:: evaluation:: asyncGeneratorEvaluation", () => { - it("return (es5)", async () => { + it("return (es5)", async (): Promise => { const result = evaluator.evaluateTypeScript(` async function * g() { return Promise.resolve(0); @@ -13,7 +13,7 @@ describe("unittests:: evaluation:: asyncGeneratorEvaluation", () => { { value: 0, done: true } ]); }); - it("return (es2015)", async () => { + it("return (es2015)", async (): Promise => { const result = evaluator.evaluateTypeScript(` async function * g() { return Promise.resolve(0); diff --git a/src/testRunner/unittests/evaluation/awaiter.ts b/src/testRunner/unittests/evaluation/awaiter.ts index 65cb4e0ead945..4ad15dc11db39 100644 --- a/src/testRunner/unittests/evaluation/awaiter.ts +++ b/src/testRunner/unittests/evaluation/awaiter.ts @@ -1,6 +1,6 @@ describe("unittests:: evaluation:: awaiter", () => { // NOTE: This could break if the ECMAScript spec ever changes the timing behavior for Promises (again) - it("await (es5)", async () => { + it("await (es5)", async (): Promise => { const result = evaluator.evaluateTypeScript(` async function a(msg: string) { await Promise.resolve(); diff --git a/src/testRunner/unittests/evaluation/forAwaitOf.ts b/src/testRunner/unittests/evaluation/forAwaitOf.ts index c7be018cbc9e6..a973fa74b951b 100644 --- a/src/testRunner/unittests/evaluation/forAwaitOf.ts +++ b/src/testRunner/unittests/evaluation/forAwaitOf.ts @@ -1,5 +1,5 @@ describe("unittests:: evaluation:: forAwaitOfEvaluation", () => { - it("sync (es5)", async () => { + it("sync (es5)", async (): Promise => { const result = evaluator.evaluateTypeScript(` let i = 0; const iterator: IterableIterator = { @@ -25,7 +25,7 @@ describe("unittests:: evaluation:: forAwaitOfEvaluation", () => { assert.strictEqual(result.output[2], 3); }); - it("sync (es2015)", async () => { + it("sync (es2015)", async (): Promise => { const result = evaluator.evaluateTypeScript(` let i = 0; const iterator: IterableIterator = { @@ -51,7 +51,7 @@ describe("unittests:: evaluation:: forAwaitOfEvaluation", () => { assert.strictEqual(result.output[2], 3); }); - it("async (es5)", async () => { + it("async (es5)", async (): Promise => { const result = evaluator.evaluateTypeScript(` let i = 0; const iterator = { @@ -77,7 +77,7 @@ describe("unittests:: evaluation:: forAwaitOfEvaluation", () => { assert.instanceOf(result.output[2], Promise); }); - it("async (es2015)", async () => { + it("async (es2015)", async (): Promise => { const result = evaluator.evaluateTypeScript(` let i = 0; const iterator = { diff --git a/src/testRunner/unittests/evaluation/objectRest.ts b/src/testRunner/unittests/evaluation/objectRest.ts index 272ba51ffbeb5..56f0180d88913 100644 --- a/src/testRunner/unittests/evaluation/objectRest.ts +++ b/src/testRunner/unittests/evaluation/objectRest.ts @@ -1,6 +1,6 @@ describe("unittests:: evaluation:: objectRest", () => { // https://github.com/microsoft/TypeScript/issues/31469 - it("side effects in property assignment", async () => { + it("side effects in property assignment", async (): Promise => { const result = evaluator.evaluateTypeScript(` const k = { a: 1, b: 2 }; const o = { a: 3, ...k, b: k.a++ }; @@ -8,7 +8,7 @@ describe("unittests:: evaluation:: objectRest", () => { `); assert.deepEqual(result.output, { a: 1, b: 1 }); }); - it("side effects in during spread", async () => { + it("side effects in during spread", async (): Promise => { const result = evaluator.evaluateTypeScript(` const k = { a: 1, get b() { l = { c: 9 }; return 2; } }; let l = { c: 3 }; @@ -17,7 +17,7 @@ describe("unittests:: evaluation:: objectRest", () => { `); assert.deepEqual(result.output, { a: 1, b: 2, c: 9 }); }); - it("trailing literal-valued object-literal", async () => { + it("trailing literal-valued object-literal", async (): Promise => { const result = evaluator.evaluateTypeScript(` const k = { a: 1 } const o = { ...k, ...{ b: 2 } }; diff --git a/src/testRunner/unittests/evaluation/optionalCall.ts b/src/testRunner/unittests/evaluation/optionalCall.ts index d6317ad0a8ca9..c8163bff4d73b 100644 --- a/src/testRunner/unittests/evaluation/optionalCall.ts +++ b/src/testRunner/unittests/evaluation/optionalCall.ts @@ -1,5 +1,5 @@ describe("unittests:: evaluation:: optionalCall", () => { - it("f?.()", async () => { + it("f?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` function f(a) { output.push(a); @@ -11,7 +11,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.isUndefined(result.output[1]); }); - it("o.f?.()", async () => { + it("o.f?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { f(a) { @@ -25,7 +25,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o); }); - it("o.x.f?.()", async () => { + it("o.x.f?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { x: { @@ -41,7 +41,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o.x); }); - it("o?.f()", async () => { + it("o?.f()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { f(a) { @@ -55,7 +55,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o); }); - it("o?.f?.()", async () => { + it("o?.f?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { f(a) { @@ -69,7 +69,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o); }); - it("o.x?.f()", async () => { + it("o.x?.f()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { x: { @@ -85,7 +85,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o.x); }); - it("o?.x.f()", async () => { + it("o?.x.f()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { x: { @@ -101,7 +101,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o.x); }); - it("o?.x?.f()", async () => { + it("o?.x?.f()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { x: { @@ -117,7 +117,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o.x); }); - it("o?.x?.f?.()", async () => { + it("o?.x?.f?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { x: { @@ -133,7 +133,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[0], 1); assert.strictEqual(result.output[1], result.o.x); }); - it("f?.()?.()", async () => { + it("f?.()?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` function g(a) { output.push(a); @@ -150,7 +150,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[1], 2); assert.isUndefined(result.output[2]); }); - it("f?.().f?.()", async () => { + it("f?.().f?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { f(a) { @@ -169,7 +169,7 @@ describe("unittests:: evaluation:: optionalCall", () => { assert.strictEqual(result.output[1], 2); assert.strictEqual(result.output[2], result.o); }); - it("f?.()?.f?.()", async () => { + it("f?.()?.f?.()", async (): Promise => { const result = evaluator.evaluateTypeScript(` export const o = { f(a) {