From 77875368036faf717ff132eea8289f1c4682cc59 Mon Sep 17 00:00:00 2001 From: Harsh Vardhan Chauhan Date: Mon, 26 Oct 2026 00:59:13 +0530 Subject: [PATCH 1/2] Fix 'this' type in object literal methods returned from async functions The contextual type of an async return expression is T | PromiseLike. Exclude the synthesized PromiseLike when deriving 'this'. --- tsc/internal/checker/checker.go | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 148cf5fde47dd..ddca6009fa23f 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -12288,6 +12288,19 @@ func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type { // for 'this' is the non-null form of the contextual type for the containing object literal or // the type of the object literal itself. if contextualType != nil { + if contextualType.flags&TypeFlagsUnion != 0 && c.isAsyncReturnExpression(containingLiteral) { + // The contextual type of an async return expression is `T | PromiseLike`; `this` is just `T`. + globalPromiseLike := c.getGlobalPromiseLikeType() + // Only remove a `PromiseLike` that was synthesized for a sibling union member `U`. + members := contextualType.Types() + contextualType = c.filterType(contextualType, func(t *Type) bool { + if t.objectFlags&ObjectFlagsReference == 0 || t.Target() != globalPromiseLike { + return true + } + args := c.getTypeArguments(t) + return len(args) != 1 || !slices.Contains(members, args[0]) + }) + } thisType = c.GetNonNullableType(contextualType) } else { thisType = c.checkExpressionCached(containingLiteral) @@ -12315,6 +12328,28 @@ func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type { return nil } +func (c *Checker) isAsyncReturnExpression(node *ast.Node) bool { + parent := node.Parent + for parent != nil && ast.IsParenthesizedExpression(parent) { + parent = parent.Parent + } + if parent == nil { + return false + } + var fn *ast.Node + switch { + case ast.IsReturnStatement(parent): + fn = ast.GetContainingFunction(parent) + case ast.IsArrowFunction(parent) && ast.SkipParentheses(parent.Body()) == node: + fn = parent + } + if fn == nil { + return false + } + flags := ast.GetFunctionFlags(fn) + return flags&ast.FunctionFlagsAsync != 0 && flags&ast.FunctionFlagsGenerator == 0 +} + func (c *Checker) checkThisExpression(node *ast.Node) *Type { // Stop at the first arrow function so that we can // tell whether 'this' needs to be captured. From 7afceffa85300e69f498e43ff18d6118e6a3632a Mon Sep 17 00:00:00 2001 From: Harsh Vardhan Chauhan Date: Wed, 28 Oct 2026 03:06:34 +0530 Subject: [PATCH 2/2] Address review: precise synthesized PromiseLike removal, keep thenables, add tests --- tsc/internal/checker/checker.go | 57 +++++--- ...syncReturnObjectLiteralThisType.errors.txt | 31 +++++ .../asyncReturnObjectLiteralThisType.symbols | 98 ++++++++++++++ .../asyncReturnObjectLiteralThisType.types | 123 ++++++++++++++++++ .../asyncReturnObjectLiteralThisType.ts | 21 +++ 5 files changed, 313 insertions(+), 17 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.types create mode 100644 tsc/testdata/tests/cases/compiler/asyncReturnObjectLiteralThisType.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 708c6dd6d214d..445d83706f2a6 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -12291,18 +12291,8 @@ func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type { // for 'this' is the non-null form of the contextual type for the containing object literal or // the type of the object literal itself. if contextualType != nil { - if contextualType.flags&TypeFlagsUnion != 0 && c.isAsyncReturnExpression(containingLiteral) { - // The contextual type of an async return expression is `T | PromiseLike`; `this` is just `T`. - globalPromiseLike := c.getGlobalPromiseLikeType() - // Only remove a `PromiseLike` that was synthesized for a sibling union member `U`. - members := contextualType.Types() - contextualType = c.filterType(contextualType, func(t *Type) bool { - if t.objectFlags&ObjectFlagsReference == 0 || t.Target() != globalPromiseLike { - return true - } - args := c.getTypeArguments(t) - return len(args) != 1 || !slices.Contains(members, args[0]) - }) + if contextualType.flags&TypeFlagsUnion != 0 { + contextualType = c.removeSynthesizedPromiseLike(containingLiteral, contextualType) } thisType = c.GetNonNullableType(contextualType) } else { @@ -12331,26 +12321,59 @@ func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type { return nil } -func (c *Checker) isAsyncReturnExpression(node *ast.Node) bool { +// The contextual type of an async function's return expression is `T | PromiseLike`, where `T` is the awaited +// contextual return type. For `this` in methods of a returned object literal, the `PromiseLike` member is an +// artifact of that wrapping, so remove exactly that synthesized member. It is kept if the literal has a `then` +// member, since the literal may then be intended as the thenable. +func (c *Checker) removeSynthesizedPromiseLike(literal *ast.Node, contextualType *Type) *Type { + fn := getAsyncFunctionReturningExpression(literal) + if fn == nil { + return contextualType + } + for _, p := range literal.AsObjectLiteralExpression().Properties.Nodes { + if p.Name() != nil { + if name, ok := ast.TryGetTextOfPropertyName(p.Name()); ok && name == "then" { + return contextualType + } + } + } + returnType := c.getContextualReturnType(fn, ContextFlagsNone) + if returnType == nil { + return contextualType + } + awaitedType := c.mapType(returnType, c.getAwaitedTypeNoAlias) + if awaitedType == nil { + return contextualType + } + synthesized := c.createPromiseLikeType(awaitedType) + return c.filterType(contextualType, func(t *Type) bool { return t != synthesized }) +} + +// Returns the async, non-generator function whose return value (a `return` operand or concise arrow body) is node. +func getAsyncFunctionReturningExpression(node *ast.Node) *ast.Node { parent := node.Parent for parent != nil && ast.IsParenthesizedExpression(parent) { + node = parent parent = parent.Parent } if parent == nil { - return false + return nil } var fn *ast.Node switch { case ast.IsReturnStatement(parent): fn = ast.GetContainingFunction(parent) - case ast.IsArrowFunction(parent) && ast.SkipParentheses(parent.Body()) == node: + case ast.IsArrowFunction(parent) && parent.Body() == node: fn = parent } if fn == nil { - return false + return nil } flags := ast.GetFunctionFlags(fn) - return flags&ast.FunctionFlagsAsync != 0 && flags&ast.FunctionFlagsGenerator == 0 + if flags&ast.FunctionFlagsAsync != 0 && flags&ast.FunctionFlagsGenerator == 0 { + return fn + } + return nil } func (c *Checker) checkThisExpression(node *ast.Node) *Type { diff --git a/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.errors.txt b/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.errors.txt new file mode 100644 index 0000000000000..dea78912b1eaa --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.errors.txt @@ -0,0 +1,31 @@ +asyncReturnObjectLiteralThisType.ts(12,45): error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member. +asyncReturnObjectLiteralThisType.ts(13,29): error TS2339: Property 'value' does not exist on type 'PromiseLike<{ value: number; }> | { value: number; }'. + Property 'value' does not exist on type 'PromiseLike<{ value: number; }>'. + + +==== asyncReturnObjectLiteralThisType.ts (2 errors) ==== + type X = { value: number; method(): void }; + declare function foo(cb: () => Promise): void; + foo(async () => ({ value: 5, method() { this satisfies X; } })); + foo(async () => { return { value: 5, method() { this satisfies X; } }; }); + foo(async function () { return { value: 5, method() { this satisfies X; } }; }); + async function f(): Promise { return { value: 1, method() { this satisfies X; } }; } + const test2: () => Promise = async () => { return { value: 5, method() { this satisfies X; } }; }; + // nullable awaited + const n1: () => Promise = async () => ({ value: 1, method() { this satisfies X; } }); + const n2: () => Promise = async () => { return { value: 1, method() { this satisfies X; } }; }; + // thenable must not get this: X + const t: () => Promise<{ value: number }> = async () => ({ + ~~~~~~~~~~~~~~ + then(resolve: any) { this.value.toFixed(); }, + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~ +!!! error TS2339: Property 'value' does not exist on type 'PromiseLike<{ value: number; }> | { value: number; }'. +!!! error TS2339: Property 'value' does not exist on type 'PromiseLike<{ value: number; }>'. + }); + ~~ +!!! error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member. + // declared PromiseLike remains + declare function g(cb: () => X | PromiseLike): void; + g(async () => ({ value: 1, method() { this satisfies X; } })); + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.symbols b/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.symbols new file mode 100644 index 0000000000000..14da480bc5700 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.symbols @@ -0,0 +1,98 @@ +//// [tests/cases/compiler/asyncReturnObjectLiteralThisType.ts] //// + +=== asyncReturnObjectLiteralThisType.ts === +type X = { value: number; method(): void }; +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 0, 10)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 0, 25)) + +declare function foo(cb: () => Promise): void; +>foo : Symbol(foo, Decl(asyncReturnObjectLiteralThisType.ts, 0, 43)) +>cb : Symbol(cb, Decl(asyncReturnObjectLiteralThisType.ts, 1, 21)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +foo(async () => ({ value: 5, method() { this satisfies X; } })); +>foo : Symbol(foo, Decl(asyncReturnObjectLiteralThisType.ts, 0, 43)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 2, 18)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 2, 28)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +foo(async () => { return { value: 5, method() { this satisfies X; } }; }); +>foo : Symbol(foo, Decl(asyncReturnObjectLiteralThisType.ts, 0, 43)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 3, 26)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 3, 36)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +foo(async function () { return { value: 5, method() { this satisfies X; } }; }); +>foo : Symbol(foo, Decl(asyncReturnObjectLiteralThisType.ts, 0, 43)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 4, 32)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 4, 42)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +async function f(): Promise { return { value: 1, method() { this satisfies X; } }; } +>f : Symbol(f, Decl(asyncReturnObjectLiteralThisType.ts, 4, 80)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 5, 41)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 5, 51)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +const test2: () => Promise = async () => { return { value: 5, method() { this satisfies X; } }; }; +>test2 : Symbol(test2, Decl(asyncReturnObjectLiteralThisType.ts, 6, 5)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 6, 54)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 6, 64)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +// nullable awaited +const n1: () => Promise = async () => ({ value: 1, method() { this satisfies X; } }); +>n1 : Symbol(n1, Decl(asyncReturnObjectLiteralThisType.ts, 8, 5)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 8, 50)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 8, 60)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +const n2: () => Promise = async () => { return { value: 1, method() { this satisfies X; } }; }; +>n2 : Symbol(n2, Decl(asyncReturnObjectLiteralThisType.ts, 9, 5)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 9, 63)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 9, 73)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +// thenable must not get this: X +const t: () => Promise<{ value: number }> = async () => ({ +>t : Symbol(t, Decl(asyncReturnObjectLiteralThisType.ts, 11, 5)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 11, 24)) + + then(resolve: any) { this.value.toFixed(); }, +>then : Symbol(then, Decl(asyncReturnObjectLiteralThisType.ts, 11, 58)) +>resolve : Symbol(resolve, Decl(asyncReturnObjectLiteralThisType.ts, 12, 7)) + +}); +// declared PromiseLike remains +declare function g(cb: () => X | PromiseLike): void; +>g : Symbol(g, Decl(asyncReturnObjectLiteralThisType.ts, 13, 3)) +>cb : Symbol(cb, Decl(asyncReturnObjectLiteralThisType.ts, 15, 19)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) +>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + +g(async () => ({ value: 1, method() { this satisfies X; } })); +>g : Symbol(g, Decl(asyncReturnObjectLiteralThisType.ts, 13, 3)) +>value : Symbol(value, Decl(asyncReturnObjectLiteralThisType.ts, 16, 16)) +>method : Symbol(method, Decl(asyncReturnObjectLiteralThisType.ts, 16, 26)) +>this : Symbol(__type, Decl(asyncReturnObjectLiteralThisType.ts, 0, 8)) +>X : Symbol(X, Decl(asyncReturnObjectLiteralThisType.ts, 0, 0)) + diff --git a/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.types b/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.types new file mode 100644 index 0000000000000..4e7c2ab6e7dff --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/asyncReturnObjectLiteralThisType.types @@ -0,0 +1,123 @@ +//// [tests/cases/compiler/asyncReturnObjectLiteralThisType.ts] //// + +=== asyncReturnObjectLiteralThisType.ts === +type X = { value: number; method(): void }; +>X : X +>value : number +>method : () => void + +declare function foo(cb: () => Promise): void; +>foo : (cb: () => Promise) => void +>cb : () => Promise + +foo(async () => ({ value: 5, method() { this satisfies X; } })); +>foo(async () => ({ value: 5, method() { this satisfies X; } })) : void +>foo : (cb: () => Promise) => void +>async () => ({ value: 5, method() { this satisfies X; } }) : () => Promise<{ value: number; method(): void; }> +>({ value: 5, method() { this satisfies X; } }) : { value: number; method(): void; } +>{ value: 5, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>5 : 5 +>method : () => void +>this satisfies X : X +>this : X + +foo(async () => { return { value: 5, method() { this satisfies X; } }; }); +>foo(async () => { return { value: 5, method() { this satisfies X; } }; }) : void +>foo : (cb: () => Promise) => void +>async () => { return { value: 5, method() { this satisfies X; } }; } : () => Promise<{ value: number; method(): void; }> +>{ value: 5, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>5 : 5 +>method : () => void +>this satisfies X : X +>this : X + +foo(async function () { return { value: 5, method() { this satisfies X; } }; }); +>foo(async function () { return { value: 5, method() { this satisfies X; } }; }) : void +>foo : (cb: () => Promise) => void +>async function () { return { value: 5, method() { this satisfies X; } }; } : () => Promise<{ value: number; method(): void; }> +>{ value: 5, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>5 : 5 +>method : () => void +>this satisfies X : X +>this : X + +async function f(): Promise { return { value: 1, method() { this satisfies X; } }; } +>f : () => Promise +>{ value: 1, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>1 : 1 +>method : () => void +>this satisfies X : X +>this : X + +const test2: () => Promise = async () => { return { value: 5, method() { this satisfies X; } }; }; +>test2 : () => Promise +>async () => { return { value: 5, method() { this satisfies X; } }; } : () => Promise<{ value: number; method(): void; }> +>{ value: 5, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>5 : 5 +>method : () => void +>this satisfies X : X +>this : X + +// nullable awaited +const n1: () => Promise = async () => ({ value: 1, method() { this satisfies X; } }); +>n1 : () => Promise +>async () => ({ value: 1, method() { this satisfies X; } }) : () => Promise<{ value: number; method(): void; }> +>({ value: 1, method() { this satisfies X; } }) : { value: number; method(): void; } +>{ value: 1, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>1 : 1 +>method : () => void +>this satisfies X : X +>this : X + +const n2: () => Promise = async () => { return { value: 1, method() { this satisfies X; } }; }; +>n2 : () => Promise +>async () => { return { value: 1, method() { this satisfies X; } }; } : () => Promise<{ value: number; method(): void; }> +>{ value: 1, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>1 : 1 +>method : () => void +>this satisfies X : X +>this : X + +// thenable must not get this: X +const t: () => Promise<{ value: number }> = async () => ({ +>t : () => Promise<{ value: number; }> +>value : number +>async () => ({ then(resolve: any) { this.value.toFixed(); },}) : () => Promise +>({ then(resolve: any) { this.value.toFixed(); },}) : { then(resolve: any): void; } +>{ then(resolve: any) { this.value.toFixed(); },} : { then(resolve: any): void; } + + then(resolve: any) { this.value.toFixed(); }, +>then : (resolve: any) => void +>resolve : any +>this.value.toFixed() : any +>this.value.toFixed : any +>this.value : any +>this : PromiseLike<{ value: number; }> | { value: number; } +>value : any +>toFixed : any + +}); +// declared PromiseLike remains +declare function g(cb: () => X | PromiseLike): void; +>g : (cb: () => X | PromiseLike) => void +>cb : () => X | PromiseLike + +g(async () => ({ value: 1, method() { this satisfies X; } })); +>g(async () => ({ value: 1, method() { this satisfies X; } })) : void +>g : (cb: () => X | PromiseLike) => void +>async () => ({ value: 1, method() { this satisfies X; } }) : () => Promise<{ value: number; method(): void; }> +>({ value: 1, method() { this satisfies X; } }) : { value: number; method(): void; } +>{ value: 1, method() { this satisfies X; } } : { value: number; method(): void; } +>value : number +>1 : 1 +>method : () => void +>this satisfies X : X +>this : X + diff --git a/tsc/testdata/tests/cases/compiler/asyncReturnObjectLiteralThisType.ts b/tsc/testdata/tests/cases/compiler/asyncReturnObjectLiteralThisType.ts new file mode 100644 index 0000000000000..03ebf6b5d698d --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/asyncReturnObjectLiteralThisType.ts @@ -0,0 +1,21 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +type X = { value: number; method(): void }; +declare function foo(cb: () => Promise): void; +foo(async () => ({ value: 5, method() { this satisfies X; } })); +foo(async () => { return { value: 5, method() { this satisfies X; } }; }); +foo(async function () { return { value: 5, method() { this satisfies X; } }; }); +async function f(): Promise { return { value: 1, method() { this satisfies X; } }; } +const test2: () => Promise = async () => { return { value: 5, method() { this satisfies X; } }; }; +// nullable awaited +const n1: () => Promise = async () => ({ value: 1, method() { this satisfies X; } }); +const n2: () => Promise = async () => { return { value: 1, method() { this satisfies X; } }; }; +// thenable must not get this: X +const t: () => Promise<{ value: number }> = async () => ({ + then(resolve: any) { this.value.toFixed(); }, +}); +// declared PromiseLike remains +declare function g(cb: () => X | PromiseLike): void; +g(async () => ({ value: 1, method() { this satisfies X; } }));