Avoid Promise<Awaited<T>> in return type inference (#45925)

This commit is contained in:
Ron Buckton
2021-09-22 19:29:07 -04:00
committed by GitHub
parent 12f72caaa5
commit 4c0a51e14b
6 changed files with 192 additions and 37 deletions

View File

@@ -25471,8 +25471,8 @@ namespace ts {
if (functionFlags & FunctionFlags.Async) { // Async function or AsyncGenerator function
// Get the awaited type without the `Awaited<T>` alias
const contextualAwaitedType = mapType(contextualReturnType, getAwaitedType);
return contextualAwaitedType && getUnionType([unwrapAwaitedType(contextualAwaitedType), createPromiseLikeType(contextualAwaitedType)]);
const contextualAwaitedType = mapType(contextualReturnType, getAwaitedTypeNoAlias);
return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]);
}
return contextualReturnType; // Regular function or Generator function
@@ -25484,8 +25484,8 @@ namespace ts {
function getContextualTypeForAwaitOperand(node: AwaitExpression, contextFlags?: ContextFlags): Type | undefined {
const contextualType = getContextualType(node, contextFlags);
if (contextualType) {
const contextualAwaitedType = getAwaitedType(contextualType);
return contextualAwaitedType && getUnionType([unwrapAwaitedType(contextualAwaitedType), createPromiseLikeType(contextualAwaitedType)]);
const contextualAwaitedType = getAwaitedTypeNoAlias(contextualType);
return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]);
}
return undefined;
}
@@ -31158,7 +31158,8 @@ namespace ts {
const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true);
if (globalPromiseType !== emptyGenericType) {
// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
promisedType = getAwaitedType(promisedType) || unknownType;
// Unwrap an `Awaited<T>` to `T` to improve inference.
promisedType = getAwaitedTypeNoAlias(unwrapAwaitedType(promisedType)) || unknownType;
return createTypeReference(globalPromiseType, [promisedType]);
}
@@ -31170,7 +31171,8 @@ namespace ts {
const globalPromiseLikeType = getGlobalPromiseLikeType(/*reportErrors*/ true);
if (globalPromiseLikeType !== emptyGenericType) {
// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
promisedType = getAwaitedType(promisedType) || unknownType;
// Unwrap an `Awaited<T>` to `T` to improve inference.
promisedType = getAwaitedTypeNoAlias(unwrapAwaitedType(promisedType)) || unknownType;
return createTypeReference(globalPromiseLikeType, [promisedType]);
}
@@ -31227,7 +31229,7 @@ namespace ts {
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which we will wrap in
// the native Promise<T> type later in this function.
returnType = checkAwaitedType(returnType, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
returnType = unwrapAwaitedType(checkAwaitedType(returnType, /*withAlias*/ false, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member));
}
}
else if (isGenerator) { // Generator or AsyncGenerator function
@@ -31460,7 +31462,7 @@ namespace ts {
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which should be wrapped in
// the native Promise<T> type by the caller.
type = checkAwaitedType(type, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
type = unwrapAwaitedType(checkAwaitedType(type, /*withAlias*/ false, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member));
}
if (type.flags & TypeFlags.Never) {
hasReturnOfTypeNever = true;
@@ -31662,7 +31664,7 @@ namespace ts {
const returnOrPromisedType = returnType && unwrapReturnType(returnType, functionFlags);
if (returnOrPromisedType) {
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function
const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
const awaitedType = checkAwaitedType(exprType, /*withAlias*/ false, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, node.body, node.body);
}
else { // Normal function
@@ -31879,7 +31881,7 @@ namespace ts {
}
const operandType = checkExpression(node.expression);
const awaitedType = checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
const awaitedType = checkAwaitedType(operandType, /*withAlias*/ true, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
if (awaitedType === operandType && awaitedType !== errorType && !(operandType.flags & TypeFlags.AnyOrUnknown)) {
addErrorOrSuggestion(/*isError*/ false, createDiagnosticForNode(node, Diagnostics.await_has_no_effect_on_the_type_of_this_expression));
}
@@ -32831,8 +32833,8 @@ namespace ts {
let wouldWorkWithAwait = false;
const errNode = errorNode || operatorToken;
if (isRelated) {
const awaitedLeftType = unwrapAwaitedType(getAwaitedType(leftType));
const awaitedRightType = unwrapAwaitedType(getAwaitedType(rightType));
const awaitedLeftType = getAwaitedTypeNoAlias(leftType);
const awaitedRightType = getAwaitedTypeNoAlias(rightType);
wouldWorkWithAwait = !(awaitedLeftType === leftType && awaitedRightType === rightType)
&& !!(awaitedLeftType && awaitedRightType)
&& isRelated(awaitedLeftType, awaitedRightType);
@@ -34914,12 +34916,15 @@ namespace ts {
/**
* Gets the "awaited type" of a type.
* @param type The type to await.
* @param withAlias When `true`, wraps the "awaited type" in `Awaited<T>` if needed.
* @remarks The "awaited type" of an expression is its "promised type" if the expression is a
* Promise-like type; otherwise, it is the type of the expression. This is used to reflect
* The runtime behavior of the `await` keyword.
*/
function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type {
const awaitedType = getAwaitedType(type, errorNode, diagnosticMessage, arg0);
function checkAwaitedType(type: Type, withAlias: boolean, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type {
const awaitedType = withAlias ?
getAwaitedType(type, errorNode, diagnosticMessage, arg0) :
getAwaitedTypeNoAlias(type, errorNode, diagnosticMessage, arg0);
return awaitedType || errorType;
}
@@ -34953,10 +34958,7 @@ namespace ts {
/**
* For a generic `Awaited<T>`, gets `T`.
*/
function unwrapAwaitedType(type: Type): Type;
function unwrapAwaitedType(type: Type | undefined): Type | undefined;
function unwrapAwaitedType(type: Type | undefined) {
if (!type) return undefined;
function unwrapAwaitedType(type: Type) {
return type.flags & TypeFlags.Union ? mapType(type, unwrapAwaitedType) :
isAwaitedTypeInstantiation(type) ? type.aliasTypeArguments[0] :
type;
@@ -35011,6 +35013,16 @@ namespace ts {
* This is used to reflect the runtime behavior of the `await` keyword.
*/
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
const awaitedType = getAwaitedTypeNoAlias(type, errorNode, diagnosticMessage, arg0);
return awaitedType && createAwaitedTypeIfNeeded(awaitedType);
}
/**
* Gets the "awaited type" of a type without introducing an `Awaited<T>` wrapper.
*
* @see {@link getAwaitedType}
*/
function getAwaitedTypeNoAlias(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
if (isTypeAny(type)) {
return type;
}
@@ -35023,14 +35035,13 @@ namespace ts {
// If we've already cached an awaited type, return a possible `Awaited<T>` for it.
const typeAsAwaitable = type as PromiseOrAwaitableType;
if (typeAsAwaitable.awaitedTypeOfType) {
return createAwaitedTypeIfNeeded(typeAsAwaitable.awaitedTypeOfType);
return typeAsAwaitable.awaitedTypeOfType;
}
// For a union, get a union of the awaited types of each constituent.
if (type.flags & TypeFlags.Union) {
const mapper = errorNode ? (constituentType: Type) => getAwaitedType(constituentType, errorNode, diagnosticMessage, arg0) : getAwaitedType;
typeAsAwaitable.awaitedTypeOfType = mapType(type, mapper);
return typeAsAwaitable.awaitedTypeOfType && createAwaitedTypeIfNeeded(typeAsAwaitable.awaitedTypeOfType);
const mapper = errorNode ? (constituentType: Type) => getAwaitedTypeNoAlias(constituentType, errorNode, diagnosticMessage, arg0) : getAwaitedTypeNoAlias;
return typeAsAwaitable.awaitedTypeOfType = mapType(type, mapper);
}
const promisedType = getPromisedTypeOfPromise(type);
@@ -35078,14 +35089,14 @@ namespace ts {
// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
// See the comments above for more information.
awaitedTypeStack.push(type.id);
const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0);
const awaitedType = getAwaitedTypeNoAlias(promisedType, errorNode, diagnosticMessage, arg0);
awaitedTypeStack.pop();
if (!awaitedType) {
return undefined;
}
return createAwaitedTypeIfNeeded(typeAsAwaitable.awaitedTypeOfType = awaitedType);
return typeAsAwaitable.awaitedTypeOfType = awaitedType;
}
// The type was not a promise, so it could not be unwrapped any further.
@@ -35111,7 +35122,7 @@ namespace ts {
return undefined;
}
return createAwaitedTypeIfNeeded(typeAsAwaitable.awaitedTypeOfType = type);
return typeAsAwaitable.awaitedTypeOfType = type;
}
/**
@@ -35161,7 +35172,7 @@ namespace ts {
if (globalPromiseType !== emptyGenericType && !isReferenceToType(returnType, globalPromiseType)) {
// The promise type was not a valid type reference to the global promise type, so we
// report an error and return the unknown type.
error(returnTypeNode, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type_Did_you_mean_to_write_Promise_0, typeToString(unwrapAwaitedType(getAwaitedType(returnType)) || voidType));
error(returnTypeNode, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type_Did_you_mean_to_write_Promise_0, typeToString(getAwaitedTypeNoAlias(returnType) || voidType));
return;
}
}
@@ -35214,7 +35225,7 @@ namespace ts {
return;
}
}
checkAwaitedType(returnType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
checkAwaitedType(returnType, /*withAlias*/ false, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
/** Check a decorator */
@@ -37495,7 +37506,7 @@ namespace ts {
const isGenerator = !!(functionFlags & FunctionFlags.Generator);
const isAsync = !!(functionFlags & FunctionFlags.Async);
return isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, isAsync) || errorType :
isAsync ? unwrapAwaitedType(getAwaitedType(returnType)) || errorType :
isAsync ? getAwaitedTypeNoAlias(returnType) || errorType :
returnType;
}
@@ -37539,7 +37550,7 @@ namespace ts {
else if (getReturnTypeFromAnnotation(container)) {
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags) ?? returnType;
const unwrappedExprType = functionFlags & FunctionFlags.Async
? checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
? checkAwaitedType(exprType, /*withAlias*/ false, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
: exprType;
if (unwrappedReturnType) {
// If the function has a return type, but promisedType is

View File

@@ -47,6 +47,22 @@ tests/cases/compiler/awaitedTypeStrictNull.ts(22,12): error TS2589: Type instant
])
}
// https://github.com/microsoft/TypeScript/issues/45924
class Api<D = {}> {
// Should result in `Promise<T>` instead of `Promise<Awaited<T>>`.
async post<T = D>() { return this.request<T>(); }
async request<D>(): Promise<D> { throw new Error(); }
}
declare const api: Api;
interface Obj { x: number }
async function fn<T>(): Promise<T extends object ? { [K in keyof T]: Obj } : Obj> {
// Per #45924, this was failing due to incorrect inference both above and here.
// Should not error.
return api.post();
}
// helps with tests where '.types' just prints out the type alias name
type _Expect<TActual extends TExpected, TExpected> = TActual;

View File

@@ -39,6 +39,22 @@ async function main() {
])
}
// https://github.com/microsoft/TypeScript/issues/45924
class Api<D = {}> {
// Should result in `Promise<T>` instead of `Promise<Awaited<T>>`.
async post<T = D>() { return this.request<T>(); }
async request<D>(): Promise<D> { throw new Error(); }
}
declare const api: Api;
interface Obj { x: number }
async function fn<T>(): Promise<T extends object ? { [K in keyof T]: Obj } : Obj> {
// Per #45924, this was failing due to incorrect inference both above and here.
// Should not error.
return api.post();
}
// helps with tests where '.types' just prints out the type alias name
type _Expect<TActual extends TExpected, TExpected> = TActual;
@@ -56,3 +72,14 @@ async function main() {
MaybePromise(true),
]);
}
// https://github.com/microsoft/TypeScript/issues/45924
class Api {
// Should result in `Promise<T>` instead of `Promise<Awaited<T>>`.
async post() { return this.request(); }
async request() { throw new Error(); }
}
async function fn() {
// Per #45924, this was failing due to incorrect inference both above and here.
// Should not error.
return api.post();
}

View File

@@ -60,21 +60,21 @@ type T12 = Awaited<Promise<Promise<number>>>;
type T13 = _Expect<Awaited<Promise<Promise<number>> | string | null>, /*expected*/ string | number | null>; // otherwise just prints T13 in types tests, which isn't very helpful
>T13 : Symbol(T13, Decl(awaitedTypeStrictNull.ts, 11, 45))
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 38, 1))
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 54, 1))
>Awaited : Symbol(Awaited, Decl(lib.es5.d.ts, --, --))
>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, --, --))
>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, --, --))
type T14 = _Expect<Awaited<Promise<Promise<number>> | string | undefined>, /*expected*/ string | number | undefined>; // otherwise just prints T14 in types tests, which isn't very helpful
>T14 : Symbol(T14, Decl(awaitedTypeStrictNull.ts, 12, 107))
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 38, 1))
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 54, 1))
>Awaited : Symbol(Awaited, Decl(lib.es5.d.ts, --, --))
>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, --, --))
>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, --, --))
type T15 = _Expect<Awaited<Promise<Promise<number>> | string | null | undefined>, /*expected*/ string | number | null | undefined>; // otherwise just prints T15 in types tests, which isn't very helpful
>T15 : Symbol(T15, Decl(awaitedTypeStrictNull.ts, 13, 117))
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 38, 1))
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 54, 1))
>Awaited : Symbol(Awaited, Decl(lib.es5.d.ts, --, --))
>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, --, --))
>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, --, --))
@@ -161,11 +161,60 @@ async function main() {
])
}
// https://github.com/microsoft/TypeScript/issues/45924
class Api<D = {}> {
>Api : Symbol(Api, Decl(awaitedTypeStrictNull.ts, 38, 1))
>D : Symbol(D, Decl(awaitedTypeStrictNull.ts, 41, 10))
// Should result in `Promise<T>` instead of `Promise<Awaited<T>>`.
async post<T = D>() { return this.request<T>(); }
>post : Symbol(Api.post, Decl(awaitedTypeStrictNull.ts, 41, 19))
>T : Symbol(T, Decl(awaitedTypeStrictNull.ts, 43, 12))
>D : Symbol(D, Decl(awaitedTypeStrictNull.ts, 41, 10))
>this.request : Symbol(Api.request, Decl(awaitedTypeStrictNull.ts, 43, 50))
>this : Symbol(Api, Decl(awaitedTypeStrictNull.ts, 38, 1))
>request : Symbol(Api.request, Decl(awaitedTypeStrictNull.ts, 43, 50))
>T : Symbol(T, Decl(awaitedTypeStrictNull.ts, 43, 12))
async request<D>(): Promise<D> { throw new Error(); }
>request : Symbol(Api.request, Decl(awaitedTypeStrictNull.ts, 43, 50))
>D : Symbol(D, Decl(awaitedTypeStrictNull.ts, 44, 15))
>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, --, --))
>D : Symbol(D, Decl(awaitedTypeStrictNull.ts, 44, 15))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
declare const api: Api;
>api : Symbol(api, Decl(awaitedTypeStrictNull.ts, 47, 13))
>Api : Symbol(Api, Decl(awaitedTypeStrictNull.ts, 38, 1))
interface Obj { x: number }
>Obj : Symbol(Obj, Decl(awaitedTypeStrictNull.ts, 47, 23))
>x : Symbol(Obj.x, Decl(awaitedTypeStrictNull.ts, 48, 15))
async function fn<T>(): Promise<T extends object ? { [K in keyof T]: Obj } : Obj> {
>fn : Symbol(fn, Decl(awaitedTypeStrictNull.ts, 48, 27))
>T : Symbol(T, Decl(awaitedTypeStrictNull.ts, 50, 18))
>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, --, --))
>T : Symbol(T, Decl(awaitedTypeStrictNull.ts, 50, 18))
>K : Symbol(K, Decl(awaitedTypeStrictNull.ts, 50, 54))
>T : Symbol(T, Decl(awaitedTypeStrictNull.ts, 50, 18))
>Obj : Symbol(Obj, Decl(awaitedTypeStrictNull.ts, 47, 23))
>Obj : Symbol(Obj, Decl(awaitedTypeStrictNull.ts, 47, 23))
// Per #45924, this was failing due to incorrect inference both above and here.
// Should not error.
return api.post();
>api.post : Symbol(Api.post, Decl(awaitedTypeStrictNull.ts, 41, 19))
>api : Symbol(api, Decl(awaitedTypeStrictNull.ts, 47, 13))
>post : Symbol(Api.post, Decl(awaitedTypeStrictNull.ts, 41, 19))
}
// helps with tests where '.types' just prints out the type alias name
type _Expect<TActual extends TExpected, TExpected> = TActual;
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 38, 1))
>TActual : Symbol(TActual, Decl(awaitedTypeStrictNull.ts, 41, 13))
>TExpected : Symbol(TExpected, Decl(awaitedTypeStrictNull.ts, 41, 39))
>TExpected : Symbol(TExpected, Decl(awaitedTypeStrictNull.ts, 41, 39))
>TActual : Symbol(TActual, Decl(awaitedTypeStrictNull.ts, 41, 13))
>_Expect : Symbol(_Expect, Decl(awaitedTypeStrictNull.ts, 54, 1))
>TActual : Symbol(TActual, Decl(awaitedTypeStrictNull.ts, 57, 13))
>TExpected : Symbol(TExpected, Decl(awaitedTypeStrictNull.ts, 57, 39))
>TExpected : Symbol(TExpected, Decl(awaitedTypeStrictNull.ts, 57, 39))
>TActual : Symbol(TActual, Decl(awaitedTypeStrictNull.ts, 57, 13))

View File

@@ -128,6 +128,42 @@ async function main() {
])
}
// https://github.com/microsoft/TypeScript/issues/45924
class Api<D = {}> {
>Api : Api<D>
// Should result in `Promise<T>` instead of `Promise<Awaited<T>>`.
async post<T = D>() { return this.request<T>(); }
>post : <T = D>() => Promise<T>
>this.request<T>() : Promise<T>
>this.request : <D>() => Promise<D>
>this : this
>request : <D>() => Promise<D>
async request<D>(): Promise<D> { throw new Error(); }
>request : <D>() => Promise<D>
>new Error() : Error
>Error : ErrorConstructor
}
declare const api: Api;
>api : Api<{}>
interface Obj { x: number }
>x : number
async function fn<T>(): Promise<T extends object ? { [K in keyof T]: Obj } : Obj> {
>fn : <T>() => Promise<T extends object ? { [K in keyof T]: Obj; } : Obj>
// Per #45924, this was failing due to incorrect inference both above and here.
// Should not error.
return api.post();
>api.post() : Promise<T extends object ? { [K in keyof T]: Obj; } : Obj>
>api.post : <T = {}>() => Promise<T>
>api : Api<{}>
>post : <T = {}>() => Promise<T>
}
// helps with tests where '.types' just prints out the type alias name
type _Expect<TActual extends TExpected, TExpected> = TActual;
>_Expect : TActual

View File

@@ -41,5 +41,21 @@ async function main() {
])
}
// https://github.com/microsoft/TypeScript/issues/45924
class Api<D = {}> {
// Should result in `Promise<T>` instead of `Promise<Awaited<T>>`.
async post<T = D>() { return this.request<T>(); }
async request<D>(): Promise<D> { throw new Error(); }
}
declare const api: Api;
interface Obj { x: number }
async function fn<T>(): Promise<T extends object ? { [K in keyof T]: Obj } : Obj> {
// Per #45924, this was failing due to incorrect inference both above and here.
// Should not error.
return api.post();
}
// helps with tests where '.types' just prints out the type alias name
type _Expect<TActual extends TExpected, TExpected> = TActual;