diff --git a/packages/typescript/src/api/async/api.ts b/packages/typescript/src/api/async/api.ts index cf2b483f0e9c6..e1eec8d4d3b51 100644 --- a/packages/typescript/src/api/async/api.ts +++ b/packages/typescript/src/api/async/api.ts @@ -1133,8 +1133,8 @@ export class Snapshot { return this.projectMap.get(data.id); } - [globalThis.Symbol.dispose](): void { - void this.dispose(); + [globalThis.Symbol.asyncDispose](): Promise { // @sync: [globalThis.Symbol.dispose](): void { + return this.dispose(); // @sync: this.dispose(); } dispose(): Promise { return this.disposePromise ??= this.disposeWorker(); @@ -1731,8 +1731,8 @@ export class Program implements FormatDiagnost this.ownedSnapshot = snapshot; } - [globalThis.Symbol.dispose](): void { - void this.dispose(); + [globalThis.Symbol.asyncDispose](): Promise { // @sync: [globalThis.Symbol.dispose](): void { + return this.dispose(); // @sync: this.dispose(); } dispose(): Promise { @@ -2175,8 +2175,8 @@ export class BuildOrchestrator { this.onDispose = onDispose; } - [globalThis.Symbol.dispose](): void { - void this.dispose(); + [globalThis.Symbol.asyncDispose](): Promise { // @sync: [globalThis.Symbol.dispose](): void { + return this.dispose(); // @sync: this.dispose(); } dispose(): Promise { return this.disposePromise ??= this.disposeWorker(); diff --git a/packages/typescript/src/api/sync/api.ts b/packages/typescript/src/api/sync/api.ts index be67bd8ca2bc0..f8976dcbb6793 100644 --- a/packages/typescript/src/api/sync/api.ts +++ b/packages/typescript/src/api/sync/api.ts @@ -1793,7 +1793,7 @@ export class Snapshot { } [globalThis.Symbol.dispose](): void { - void this.dispose(); + this.dispose(); } get dispose(): { (): void; @@ -2924,7 +2924,7 @@ export class Program implements FormatDiagnost } [globalThis.Symbol.dispose](): void { - void this.dispose(); + this.dispose(); } get dispose(): { @@ -3936,7 +3936,7 @@ export class BuildOrchestrator { } [globalThis.Symbol.dispose](): void { - void this.dispose(); + this.dispose(); } get dispose(): { (): void; diff --git a/packages/typescript/test/async/api.test.ts b/packages/typescript/test/async/api.test.ts index dc4547088c284..f9245bdb4d216 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -151,9 +151,9 @@ import { const concurrency = areTestsFiltered(); describe("API", { concurrency }, () => { - test("getCurrentLanguageServerSnapshot is LSP-only", () => { + test("getCurrentLanguageServerSnapshot is LSP-only", async () => { if (!!false) { - const standalone = new API(); + await using standalone = new API(); // @ts-expect-error The standalone API has no canonical language server state. void standalone.getCurrentLanguageServerSnapshot(); @@ -729,7 +729,7 @@ describe("API", { concurrency }, () => { "/src/index.ts": `export const value: string = 1;`, }); - const program = await api.createProgram(["/src/index.ts"], { noLib: true, strict: true }); + await using program = await api.createProgram(["/src/index.ts"], { noLib: true, strict: true }); assert.deepEqual(program.getCompilerOptions(), { noLib: true, strict: true }); assert.deepEqual(await program.getSourceFileNames(), ["/src/index.ts"]); @@ -1617,7 +1617,7 @@ describe("BuildOrchestrator", () => { const { api: disposableApi } = spawnAPIWithFS({ ...files }); await using api = disposableApi; const options = await api.parseCommandLine([]); - const orchestrator = await api.createBuildOrchestrator( + await using orchestrator = await api.createBuildOrchestrator( ["/a/tsconfig.json"], { cwd: "/", ...options }, ); @@ -3272,11 +3272,11 @@ describe("Snapshot disposal", { concurrency }, () => { assert.ok(snapshot.isDisposed()); }); - test("api.close waits for disposal started by using", async () => { + test("api.close waits for disposal started by await using", async () => { const api = spawnAPI(); let snapshot: Snapshot; { - using disposableSnapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + await using disposableSnapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); snapshot = disposableSnapshot; } assert.ok(snapshot.isDisposed()); @@ -4994,7 +4994,7 @@ describe("readFile callback semantics", { concurrency }, () => { const callbacks: FileSystemCallbacks = { ...fs, realpath }; await using api = new API({ cwd: "/", fs: callbacks }); - using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); assert.ok( (await snapshot.getConfiguredProject("/tsconfig.json")!.program.getSourceFileNames()) .includes(toRootedFilePath("/node_modules/pkg/index.d.ts", undefined)), @@ -5212,7 +5212,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { fs, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: { kind: "full", @@ -5237,7 +5237,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { await using api = new API({ cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: createFileSystemWithLib(Object.entries({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true }, files: ["src/main.ts"] }), @@ -5269,7 +5269,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), collectTiming: true, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openFiles: files.map(([document]) => document), fileSystem: createFileSystem(files), }); @@ -5311,7 +5311,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { fs, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: { kind: "layer", @@ -5344,7 +5344,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { }), }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: createFileSystemLayer([ ["/tsconfig.json", JSON.stringify({ compilerOptions: { noLib: true }, include: ["src/**/*.ts"] })], @@ -5377,7 +5377,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { }, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/project/tsconfig.json", fileSystem: { kind: "full", @@ -5418,7 +5418,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { }, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/project/tsconfig.json", fileSystem: { kind: "full", @@ -5444,7 +5444,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { await using api = new API({ cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: createFileSystem(Object.entries({ "/tsconfig.json": JSON.stringify({ @@ -5458,7 +5458,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { })), }); - using updated = await snapshot.update({ + await using updated = await snapshot.update({ ensurePrograms: true, fileSystem: createFileSystemLayer( Object.entries({ @@ -5476,10 +5476,10 @@ describe("updateSnapshot file systems", { concurrency }, () => { assert.equal((await project.program.getSourceFile("/src/added.ts"))?.text, `export const added = true;`); assert.equal(await project.program.getSourceFile("/src/remove.ts"), undefined); assert.equal(await project.program.getSourceFile("/src/removed/gone.ts"), undefined); - using fork = await snapshot.update({ ensurePrograms: true }); + await using fork = await snapshot.update({ ensurePrograms: true }); assert.equal((await fork.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/change.ts"))?.text, `export const version = "old";`); - using updatedAgain = await updated.update({ + await using updatedAgain = await updated.update({ ensurePrograms: true, fileSystem: createFileSystemLayer( Object.entries({ @@ -5536,8 +5536,8 @@ describe("updateSnapshot file systems", { concurrency }, () => { cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), fs: host, }); - using snapshot = await api.createSnapshot(); - using replaced = await snapshot.update({ + await using snapshot = await api.createSnapshot(); + await using replaced = await snapshot.update({ ensurePrograms: true, openProject: "/tsconfig.json", fileSystem: createFileSystem([ @@ -5554,7 +5554,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { await using api = new API({ cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: createFileSystem( Object.entries({ @@ -5571,7 +5571,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { ), }); - using updated = await snapshot.update({ + await using updated = await snapshot.update({ ensurePrograms: true, fileSystem: createFileSystemLayer( Object.entries({ @@ -5606,7 +5606,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { removeFile: serverFS.useOS, }, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: createFileSystem(Object.entries({ "/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true, outDir: "/out", rootDir: "/src" }, files: ["src/main.ts"] }), @@ -5624,7 +5624,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { }); assert.deepEqual(hostWrites, []); - using updated = await snapshot.update({ fileSystem: result.fileSystem!, openFiles: ["/out/main.js"] }); + await using updated = await snapshot.update({ fileSystem: result.fileSystem!, openFiles: ["/out/main.js"] }); const outputProject = await updated.getDefaultProjectForFile("/out/main.js"); assert.equal((await updated.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/main.ts"))?.text, `export const value: number = 1;`); assert.equal((await outputProject?.program.getSourceFile("/out/main.js"))?.text, `export const value = 1;\n`); @@ -5636,7 +5636,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), fs: host, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/tsconfig.json", fileSystem: createFileSystemLayer(Object.entries({ "/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true, outDir: "/out", rootDir: "/src" }, files: ["src/main.ts"] }), @@ -5676,7 +5676,7 @@ describe("updateSnapshot file systems", { concurrency }, () => { }, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/project/tsconfig.json", fileSystem: { kind: "full", @@ -5711,14 +5711,14 @@ describe("updateSnapshot file systems", { concurrency }, () => { cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), fs: host, }); - using snapshot = await api.createSnapshot({ + await using snapshot = await api.createSnapshot({ openProject: "/project/tsconfig.json", fileSystem: createFileSystem([ ["/project/tsconfig.json", JSON.stringify({ compilerOptions: { noLib: true, moduleResolution: "node" }, files: ["index.ts"] })], ["/project/index.ts", `import { value } from "pkg"; export { value };`], ]), }); - using updated = await snapshot.update({ + await using updated = await snapshot.update({ ensurePrograms: true, fileSystem: createFileSystemLayer([], { symlinks: { diff --git a/packages/typescript/test/sync/api.test.ts b/packages/typescript/test/sync/api.test.ts index b720b2472b3e8..b9f69d2e14aba 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -161,7 +161,7 @@ const concurrency = areTestsFiltered(); describe("API", { concurrency }, () => { test("getCurrentLanguageServerSnapshot is LSP-only", () => { if (!!false) { - const standalone = new API(); + using standalone = new API(); // @ts-expect-error The standalone API has no canonical language server state. void standalone.getCurrentLanguageServerSnapshot(); @@ -716,7 +716,7 @@ describe("API", { concurrency }, () => { "/src/index.ts": `export const value: string = 1;`, }); - const program = api.createProgram(["/src/index.ts"], { noLib: true, strict: true }); + using program = api.createProgram(["/src/index.ts"], { noLib: true, strict: true }); assert.deepEqual(program.getCompilerOptions(), { noLib: true, strict: true }); assert.deepEqual(program.getSourceFileNames(), ["/src/index.ts"]); @@ -1483,7 +1483,7 @@ describe("BuildOrchestrator", () => { const { api: disposableApi } = spawnAPIWithFS({ ...files }); using api = disposableApi; const options = api.parseCommandLine([]); - const orchestrator = api.createBuildOrchestrator( + using orchestrator = api.createBuildOrchestrator( ["/a/tsconfig.json"], { cwd: "/", ...options }, ); @@ -3130,7 +3130,7 @@ describe("Snapshot disposal", { concurrency }, () => { assert.ok(snapshot.isDisposed()); }); - test("api.close waits for disposal started by using", () => { + test("api.close waits for disposal started by await using", () => { const api = spawnAPI(); let snapshot: Snapshot; { diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 1625a860a592e..d592da7b1c35d 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -6098,8 +6098,17 @@ func (c *Checker) checkVariableLikeDeclaration(node *ast.Node) { globalDisposableType := c.getGlobalDisposableType() if globalDisposableType != c.emptyObjectType { optionalDisposableType := c.getUnionType([]*Type{globalDisposableType, c.nullType, c.undefinedType}) - c.checkTypeAssignableTo(c.widenTypeForVariableLikeDeclaration(initializerType, node, false), optionalDisposableType, initializer, - diagnostics.The_initializer_of_a_using_declaration_must_be_either_an_object_with_a_Symbol_dispose_method_or_be_null_or_undefined) + widenedInitializerType := c.widenTypeForVariableLikeDeclaration(initializerType, node, false) + var diags []*ast.Diagnostic + if !c.checkTypeAssignableToEx(widenedInitializerType, optionalDisposableType, initializer, + diagnostics.The_initializer_of_a_using_declaration_must_be_either_an_object_with_a_Symbol_dispose_method_or_be_null_or_undefined, &diags) { + globalAsyncDisposableType := c.getGlobalAsyncDisposableType() + optionalAsyncDisposableType := c.getUnionType([]*Type{globalAsyncDisposableType, c.nullType, c.undefinedType}) + if globalAsyncDisposableType != c.emptyObjectType && c.isTypeAssignableTo(widenedInitializerType, optionalAsyncDisposableType) { + diags[0].AddMessageChain(ast.NewDiagnosticChain(nil, diagnostics.This_initializer_has_a_Symbol_asyncDispose_method_Did_you_mean_to_use_await_using)) + } + c.addDiagnostic(diags[0]) + } } } } diff --git a/tsc/internal/diagnostics/diagnosticMessages.generated.json b/tsc/internal/diagnostics/diagnosticMessages.generated.json index cf66284f21b0a..e28fccc123b35 100644 --- a/tsc/internal/diagnostics/diagnosticMessages.generated.json +++ b/tsc/internal/diagnostics/diagnosticMessages.generated.json @@ -1949,6 +1949,7 @@ "Source_phase_imports_are_only_supported_when_the_module_option_is_set_to_esnext_nodenext_or_preserve_18113": "Source phase imports are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'.", "Optional_chaining_cannot_be_used_with_import_source_18114": "Optional chaining cannot be used with 'import.source'.", "Source_phase_imports_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls_18115": "Source phase imports are not allowed on statements that compile to CommonJS 'require' calls.", + "This_initializer_has_a_Symbol_asyncDispose_method_Did_you_mean_to_use_await_using_18116": "This initializer has a '[Symbol.asyncDispose]()' method. Did you mean to use 'await using'?", "nodenext_if_module_is_nodenext_node16_if_module_is_node16_or_node18_otherwise_bundler_69010": "`nodenext` if `module` is `nodenext`; `node16` if `module` is `node16` or `node18`; otherwise, `bundler`.", "File_is_a_CommonJS_module_it_may_be_converted_to_an_ES_module_80001": "File is a CommonJS module; it may be converted to an ES module.", "This_constructor_function_may_be_converted_to_a_class_declaration_80002": "This constructor function may be converted to a class declaration.", diff --git a/tsc/internal/diagnostics/diagnosticMessages.json b/tsc/internal/diagnostics/diagnosticMessages.json index be00151a57d6a..663469b8ec981 100644 --- a/tsc/internal/diagnostics/diagnosticMessages.json +++ b/tsc/internal/diagnostics/diagnosticMessages.json @@ -8915,5 +8915,9 @@ "Source phase imports are not allowed on statements that compile to CommonJS 'require' calls.": { "category": "Error", "code": 18115 + }, + "This initializer has a '[Symbol.asyncDispose]()' method. Did you mean to use 'await using'?": { + "category": "Error", + "code": 18116 } } diff --git a/tsc/internal/diagnostics/diagnostics_generated.go b/tsc/internal/diagnostics/diagnostics_generated.go index 8a28f92aa3f1f..818fe9668e891 100644 --- a/tsc/internal/diagnostics/diagnostics_generated.go +++ b/tsc/internal/diagnostics/diagnostics_generated.go @@ -3902,6 +3902,8 @@ var Optional_chaining_cannot_be_used_with_import_source = &Message{code: 18114, var Source_phase_imports_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls = &Message{code: 18115, category: CategoryError, key: "Source_phase_imports_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls_18115", text: "Source phase imports are not allowed on statements that compile to CommonJS 'require' calls."} +var This_initializer_has_a_Symbol_asyncDispose_method_Did_you_mean_to_use_await_using = &Message{code: 18116, category: CategoryError, key: "This_initializer_has_a_Symbol_asyncDispose_method_Did_you_mean_to_use_await_using_18116", text: "This initializer has a '[Symbol.asyncDispose]()' method. Did you mean to use 'await using'?"} + var X_nodenext_if_module_is_nodenext_node16_if_module_is_node16_or_node18_otherwise_bundler = &Message{code: 69010, category: CategoryMessage, key: "nodenext_if_module_is_nodenext_node16_if_module_is_node16_or_node18_otherwise_bundler_69010", text: "`nodenext` if `module` is `nodenext`; `node16` if `module` is `node16` or `node18`; otherwise, `bundler`."} var File_is_a_CommonJS_module_it_may_be_converted_to_an_ES_module = &Message{code: 80001, category: CategorySuggestion, key: "File_is_a_CommonJS_module_it_may_be_converted_to_an_ES_module_80001", text: "File is a CommonJS module; it may be converted to an ES module."} @@ -6393,6 +6395,7 @@ var allMessages = [...]**Message{ &Source_phase_imports_are_only_supported_when_the_module_option_is_set_to_esnext_nodenext_or_preserve, &Optional_chaining_cannot_be_used_with_import_source, &Source_phase_imports_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls, + &This_initializer_has_a_Symbol_asyncDispose_method_Did_you_mean_to_use_await_using, &X_nodenext_if_module_is_nodenext_node16_if_module_is_node16_or_node18_otherwise_bundler, &File_is_a_CommonJS_module_it_may_be_converted_to_an_ES_module, &This_constructor_function_may_be_converted_to_a_class_declaration, diff --git a/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.errors.txt b/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.errors.txt new file mode 100644 index 0000000000000..85f046404c8bd --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.errors.txt @@ -0,0 +1,31 @@ +usingAsyncDisposable.ts(6,23): error TS2741: Property '[Symbol.dispose]' is missing in type 'AsyncDisposable' but required in type 'Disposable'. + This initializer has a '[Symbol.asyncDispose]()' method. Did you mean to use 'await using'? +usingAsyncDisposable.ts(7,31): error TS2850: The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'. + Property '[Symbol.dispose]' is missing in type 'AsyncDisposable' but required in type 'Disposable'. + This initializer has a '[Symbol.asyncDispose]()' method. Did you mean to use 'await using'? + + +==== usingAsyncDisposable.ts (2 errors) ==== + declare const asyncDisposable: AsyncDisposable; + declare const optionalAsyncDisposable: AsyncDisposable | null | undefined; + declare const disposable: Disposable; + declare const both: AsyncDisposable & Disposable; + + using asyncResource = asyncDisposable; + ~~~~~~~~~~~~~~~ +!!! error TS2741: Property '[Symbol.dispose]' is missing in type 'AsyncDisposable' but required in type 'Disposable'. +!!! error TS2741: This initializer has a '[Symbol.asyncDispose]()' method. Did you mean to use 'await using'? +!!! related TS2728 lib.esnext.disposable.d.ts:--:--: '[Symbol.dispose]' is declared here. + using optionalAsyncResource = optionalAsyncDisposable; + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2850: The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'. +!!! error TS2850: Property '[Symbol.dispose]' is missing in type 'AsyncDisposable' but required in type 'Disposable'. +!!! error TS2850: This initializer has a '[Symbol.asyncDispose]()' method. Did you mean to use 'await using'? +!!! related TS2728 lib.esnext.disposable.d.ts:--:--: '[Symbol.dispose]' is declared here. + using syncResource = disposable; + using eitherResource = both; + + await using awaitedAsyncResource = asyncDisposable; + + export {}; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.symbols b/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.symbols new file mode 100644 index 0000000000000..a4c67154fbf9b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.symbols @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/usingAsyncDisposable.ts] //// + +=== usingAsyncDisposable.ts === +declare const asyncDisposable: AsyncDisposable; +>asyncDisposable : Symbol(asyncDisposable, Decl(usingAsyncDisposable.ts, 0, 13)) +>AsyncDisposable : Symbol(AsyncDisposable, Decl(lib.esnext.disposable.d.ts, --, --)) + +declare const optionalAsyncDisposable: AsyncDisposable | null | undefined; +>optionalAsyncDisposable : Symbol(optionalAsyncDisposable, Decl(usingAsyncDisposable.ts, 1, 13)) +>AsyncDisposable : Symbol(AsyncDisposable, Decl(lib.esnext.disposable.d.ts, --, --)) + +declare const disposable: Disposable; +>disposable : Symbol(disposable, Decl(usingAsyncDisposable.ts, 2, 13)) +>Disposable : Symbol(Disposable, Decl(lib.esnext.disposable.d.ts, --, --)) + +declare const both: AsyncDisposable & Disposable; +>both : Symbol(both, Decl(usingAsyncDisposable.ts, 3, 13)) +>AsyncDisposable : Symbol(AsyncDisposable, Decl(lib.esnext.disposable.d.ts, --, --)) +>Disposable : Symbol(Disposable, Decl(lib.esnext.disposable.d.ts, --, --)) + +using asyncResource = asyncDisposable; +>asyncResource : Symbol(asyncResource, Decl(usingAsyncDisposable.ts, 5, 5)) +>asyncDisposable : Symbol(asyncDisposable, Decl(usingAsyncDisposable.ts, 0, 13)) + +using optionalAsyncResource = optionalAsyncDisposable; +>optionalAsyncResource : Symbol(optionalAsyncResource, Decl(usingAsyncDisposable.ts, 6, 5)) +>optionalAsyncDisposable : Symbol(optionalAsyncDisposable, Decl(usingAsyncDisposable.ts, 1, 13)) + +using syncResource = disposable; +>syncResource : Symbol(syncResource, Decl(usingAsyncDisposable.ts, 7, 5)) +>disposable : Symbol(disposable, Decl(usingAsyncDisposable.ts, 2, 13)) + +using eitherResource = both; +>eitherResource : Symbol(eitherResource, Decl(usingAsyncDisposable.ts, 8, 5)) +>both : Symbol(both, Decl(usingAsyncDisposable.ts, 3, 13)) + +await using awaitedAsyncResource = asyncDisposable; +>awaitedAsyncResource : Symbol(awaitedAsyncResource, Decl(usingAsyncDisposable.ts, 10, 11)) +>asyncDisposable : Symbol(asyncDisposable, Decl(usingAsyncDisposable.ts, 0, 13)) + +export {}; + diff --git a/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.types b/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.types new file mode 100644 index 0000000000000..3fd50f0c44e58 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/usingAsyncDisposable.types @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/usingAsyncDisposable.ts] //// + +=== usingAsyncDisposable.ts === +declare const asyncDisposable: AsyncDisposable; +>asyncDisposable : AsyncDisposable + +declare const optionalAsyncDisposable: AsyncDisposable | null | undefined; +>optionalAsyncDisposable : AsyncDisposable | null | undefined + +declare const disposable: Disposable; +>disposable : Disposable + +declare const both: AsyncDisposable & Disposable; +>both : AsyncDisposable & Disposable + +using asyncResource = asyncDisposable; +>asyncResource : AsyncDisposable +>asyncDisposable : AsyncDisposable + +using optionalAsyncResource = optionalAsyncDisposable; +>optionalAsyncResource : AsyncDisposable | null | undefined +>optionalAsyncDisposable : AsyncDisposable | null | undefined + +using syncResource = disposable; +>syncResource : Disposable +>disposable : Disposable + +using eitherResource = both; +>eitherResource : AsyncDisposable & Disposable +>both : AsyncDisposable & Disposable + +await using awaitedAsyncResource = asyncDisposable; +>awaitedAsyncResource : AsyncDisposable +>asyncDisposable : AsyncDisposable + +export {}; + diff --git a/tsc/testdata/tests/cases/compiler/usingAsyncDisposable.ts b/tsc/testdata/tests/cases/compiler/usingAsyncDisposable.ts new file mode 100644 index 0000000000000..1428d05f4c966 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/usingAsyncDisposable.ts @@ -0,0 +1,19 @@ +// @strict: true +// @target: esnext +// @module: esnext +// @lib: esnext +// @noEmit: true + +declare const asyncDisposable: AsyncDisposable; +declare const optionalAsyncDisposable: AsyncDisposable | null | undefined; +declare const disposable: Disposable; +declare const both: AsyncDisposable & Disposable; + +using asyncResource = asyncDisposable; +using optionalAsyncResource = optionalAsyncDisposable; +using syncResource = disposable; +using eitherResource = both; + +await using awaitedAsyncResource = asyncDisposable; + +export {};