diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 75be36474222f..c4c86f26e4372 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32613,7 +32613,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // types as well as arguments to the left in a function call. return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); } - if (inferenceContext?.returnMapper) { + if (!(contextFlags! & ContextFlags.SkipReturnMapper) && inferenceContext?.returnMapper) { // For other purposes (e.g. determining whether to produce literal types) we only // incorporate inferences made from the return type in a function call. We remove // the 'boolean' type from the contextual type such that contextually typed boolean @@ -33208,6 +33208,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isSpreadElement(parent) && isCallOrNewExpression(parent.parent); } + function givesTupleContext(type: Type | undefined) { + return !!type && someType(type, t => isTupleLikeType(t) || isGenericMappedType(t) && !t.nameType && !!getHomomorphicTypeVariable(t.target as MappedType || t)); + } + function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined, forceTuple: boolean | undefined): Type { const elements = node.elements; const elementCount = elements.length; @@ -33217,7 +33221,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const inDestructuringPattern = isAssignmentTarget(node); const inConstContext = isConstContext(node); const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined); - const inTupleContext = isSpreadIntoCallOrNew(node) || !!contextualType && someType(contextualType, t => isTupleLikeType(t) || isGenericMappedType(t) && !t.nameType && !!getHomomorphicTypeVariable(t.target as MappedType || t)); + const inTupleContext = isSpreadIntoCallOrNew(node) || givesTupleContext(contextualType) || givesTupleContext(getApparentTypeOfContextualType(node, ContextFlags.SkipReturnMapper)); let hasOmittedExpression = false; for (let i = 0; i < elementCount; i++) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1cfe3e04ba68d..1fc7111fec9bc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5529,6 +5529,7 @@ export const enum ContextFlags { NoConstraints = 1 << 1, // Don't obtain type variable constraints Completions = 1 << 2, // Ignore inference to current node and parent nodes out to the containing call for completions SkipBindingPatterns = 1 << 3, // Ignore contextual types applied by binding patterns + SkipReturnMapper = 1 << 4, } // NOTE: If modifying this enum, must modify `TypeFormatFlags` too! diff --git a/tests/baselines/reference/inferFromGenericFunctionReturnTypes3.types b/tests/baselines/reference/inferFromGenericFunctionReturnTypes3.types index e20de1c2bbd86..ab844b7b31b26 100644 --- a/tests/baselines/reference/inferFromGenericFunctionReturnTypes3.types +++ b/tests/baselines/reference/inferFromGenericFunctionReturnTypes3.types @@ -615,20 +615,20 @@ type F = () => Promise>; const f1: F = () => { >f1 : F > : ^ ->() => { return Promise.all([ { name: "David Gomes", age: 23, position: "GOALKEEPER", }, { name: "Cristiano Ronaldo", age: 33, position: "STRIKER", } ]);} : () => Promise<({ name: string; age: number; position: "GOALKEEPER"; } | { name: string; age: number; position: "STRIKER"; })[]> -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => { return Promise.all([ { name: "David Gomes", age: 23, position: "GOALKEEPER", }, { name: "Cristiano Ronaldo", age: 33, position: "STRIKER", } ]);} : () => Promise<[{ name: string; age: number; position: "GOALKEEPER"; }, { name: string; age: number; position: "STRIKER"; }]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return Promise.all([ ->Promise.all([ { name: "David Gomes", age: 23, position: "GOALKEEPER", }, { name: "Cristiano Ronaldo", age: 33, position: "STRIKER", } ]) : Promise<({ name: string; age: number; position: "GOALKEEPER"; } | { name: string; age: number; position: "STRIKER"; })[]> -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Promise.all([ { name: "David Gomes", age: 23, position: "GOALKEEPER", }, { name: "Cristiano Ronaldo", age: 33, position: "STRIKER", } ]) : Promise<[{ name: string; age: number; position: "GOALKEEPER"; }, { name: string; age: number; position: "STRIKER"; }]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >Promise.all : { (values: Iterable>): Promise[]>; (values: T): Promise<{ -readonly [P in keyof T]: Awaited; }>; } > : ^^^ ^^ ^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^^ ^^^ >Promise : PromiseConstructor > : ^^^^^^^^^^^^^^^^^^ >all : { (values: Iterable>): Promise[]>; (values: T): Promise<{ -readonly [P in keyof T]: Awaited; }>; } > : ^^^ ^^ ^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^^ ^^^ ->[ { name: "David Gomes", age: 23, position: "GOALKEEPER", }, { name: "Cristiano Ronaldo", age: 33, position: "STRIKER", } ] : ({ name: string; age: number; position: "GOALKEEPER"; } | { name: string; age: number; position: "STRIKER"; })[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>[ { name: "David Gomes", age: 23, position: "GOALKEEPER", }, { name: "Cristiano Ronaldo", age: 33, position: "STRIKER", } ] : [{ name: string; age: number; position: "GOALKEEPER"; }, { name: string; age: number; position: "STRIKER"; }] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ { >{ name: "David Gomes", age: 23, position: "GOALKEEPER", } : { name: string; age: number; position: "GOALKEEPER"; } > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/baselines/reference/returnTypeContextualTupleTyping1.symbols b/tests/baselines/reference/returnTypeContextualTupleTyping1.symbols new file mode 100644 index 0000000000000..9cc4bb1316a05 --- /dev/null +++ b/tests/baselines/reference/returnTypeContextualTupleTyping1.symbols @@ -0,0 +1,134 @@ +//// [tests/cases/compiler/returnTypeContextualTupleTyping1.ts] //// + +=== returnTypeContextualTupleTyping1.ts === +// https://github.com/microsoft/TypeScript/issues/62071 + +declare function getNum(): Promise; +>getNum : Symbol(getNum, Decl(returnTypeContextualTupleTyping1.ts, 0, 0)) +>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, --, --)) + +declare function getStr(): Promise; +>getStr : Symbol(getStr, Decl(returnTypeContextualTupleTyping1.ts, 2, 43)) +>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, --, --)) + +declare function useTuple(tuple: [number, string]): void; +>useTuple : Symbol(useTuple, Decl(returnTypeContextualTupleTyping1.ts, 3, 43)) +>tuple : Symbol(tuple, Decl(returnTypeContextualTupleTyping1.ts, 4, 26)) + +const p = Promise.resolve([]) +>p : Symbol(p, Decl(returnTypeContextualTupleTyping1.ts, 6, 5)) +>Promise.resolve([]) .then(() => Promise.all([getNum(), getStr()])) .then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>Promise.resolve([]) .then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.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, --, --)) +>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) + + .then(() => Promise.all([getNum(), getStr()])) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>Promise.all : Symbol(PromiseConstructor.all, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.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, --, --)) +>all : Symbol(PromiseConstructor.all, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>getNum : Symbol(getNum, Decl(returnTypeContextualTupleTyping1.ts, 0, 0)) +>getStr : Symbol(getStr, Decl(returnTypeContextualTupleTyping1.ts, 2, 43)) + + .then(useTuple); // ok +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>useTuple : Symbol(useTuple, Decl(returnTypeContextualTupleTyping1.ts, 3, 43)) + +// same as above but without relying as much on builtin libs +interface MyPromise { +>MyPromise : Symbol(MyPromise, Decl(returnTypeContextualTupleTyping1.ts, 8, 18)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 11, 20)) + + then< +>then : Symbol(MyPromise.then, Decl(returnTypeContextualTupleTyping1.ts, 11, 24)) + + TResult1 = T, // outer type parameter used as default here is important for the test +>TResult1 : Symbol(TResult1, Decl(returnTypeContextualTupleTyping1.ts, 12, 7)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 11, 20)) + + TResult2 = never, +>TResult2 : Symbol(TResult2, Decl(returnTypeContextualTupleTyping1.ts, 13, 17)) + + >( + onfulfilled?: +>onfulfilled : Symbol(onfulfilled, Decl(returnTypeContextualTupleTyping1.ts, 15, 4)) + + | ((value: T) => TResult1 | PromiseLike) +>value : Symbol(value, Decl(returnTypeContextualTupleTyping1.ts, 17, 10)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 11, 20)) +>TResult1 : Symbol(TResult1, Decl(returnTypeContextualTupleTyping1.ts, 12, 7)) +>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --)) +>TResult1 : Symbol(TResult1, Decl(returnTypeContextualTupleTyping1.ts, 12, 7)) + + | undefined + | null, + onrejected?: +>onrejected : Symbol(onrejected, Decl(returnTypeContextualTupleTyping1.ts, 19, 13)) + + | ((reason: any) => TResult2 | PromiseLike) +>reason : Symbol(reason, Decl(returnTypeContextualTupleTyping1.ts, 21, 10)) +>TResult2 : Symbol(TResult2, Decl(returnTypeContextualTupleTyping1.ts, 13, 17)) +>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --)) +>TResult2 : Symbol(TResult2, Decl(returnTypeContextualTupleTyping1.ts, 13, 17)) + + | undefined + | null, + ): MyPromise; +>MyPromise : Symbol(MyPromise, Decl(returnTypeContextualTupleTyping1.ts, 8, 18)) +>TResult1 : Symbol(TResult1, Decl(returnTypeContextualTupleTyping1.ts, 12, 7)) +>TResult2 : Symbol(TResult2, Decl(returnTypeContextualTupleTyping1.ts, 13, 17)) +} + +declare function resolve(value: T): MyPromise>; +>resolve : Symbol(resolve, Decl(returnTypeContextualTupleTyping1.ts, 25, 1), Decl(returnTypeContextualTupleTyping1.ts, 27, 61)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 27, 25)) +>value : Symbol(value, Decl(returnTypeContextualTupleTyping1.ts, 27, 28)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 27, 25)) +>MyPromise : Symbol(MyPromise, Decl(returnTypeContextualTupleTyping1.ts, 8, 18)) +>Awaited : Symbol(Awaited, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 27, 25)) + +declare function resolve(value: T | PromiseLike): MyPromise>; +>resolve : Symbol(resolve, Decl(returnTypeContextualTupleTyping1.ts, 25, 1), Decl(returnTypeContextualTupleTyping1.ts, 27, 61)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 28, 25)) +>value : Symbol(value, Decl(returnTypeContextualTupleTyping1.ts, 28, 28)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 28, 25)) +>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 28, 25)) +>MyPromise : Symbol(MyPromise, Decl(returnTypeContextualTupleTyping1.ts, 8, 18)) +>Awaited : Symbol(Awaited, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 28, 25)) + +declare function all( +>all : Symbol(all, Decl(returnTypeContextualTupleTyping1.ts, 28, 78)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 30, 21)) + + values: T, +>values : Symbol(values, Decl(returnTypeContextualTupleTyping1.ts, 30, 56)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 30, 21)) + +): MyPromise<{ -readonly [P in keyof T]: Awaited }>; +>MyPromise : Symbol(MyPromise, Decl(returnTypeContextualTupleTyping1.ts, 8, 18)) +>P : Symbol(P, Decl(returnTypeContextualTupleTyping1.ts, 32, 26)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 30, 21)) +>Awaited : Symbol(Awaited, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(returnTypeContextualTupleTyping1.ts, 30, 21)) +>P : Symbol(P, Decl(returnTypeContextualTupleTyping1.ts, 32, 26)) + +const p2 = resolve([]) +>p2 : Symbol(p2, Decl(returnTypeContextualTupleTyping1.ts, 34, 5)) +>resolve([]) .then(() => all([getNum(), getStr()])) .then : Symbol(MyPromise.then, Decl(returnTypeContextualTupleTyping1.ts, 11, 24)) +>resolve([]) .then : Symbol(MyPromise.then, Decl(returnTypeContextualTupleTyping1.ts, 11, 24)) +>resolve : Symbol(resolve, Decl(returnTypeContextualTupleTyping1.ts, 25, 1), Decl(returnTypeContextualTupleTyping1.ts, 27, 61)) + + .then(() => all([getNum(), getStr()])) +>then : Symbol(MyPromise.then, Decl(returnTypeContextualTupleTyping1.ts, 11, 24)) +>all : Symbol(all, Decl(returnTypeContextualTupleTyping1.ts, 28, 78)) +>getNum : Symbol(getNum, Decl(returnTypeContextualTupleTyping1.ts, 0, 0)) +>getStr : Symbol(getStr, Decl(returnTypeContextualTupleTyping1.ts, 2, 43)) + + .then(useTuple); // ok +>then : Symbol(MyPromise.then, Decl(returnTypeContextualTupleTyping1.ts, 11, 24)) +>useTuple : Symbol(useTuple, Decl(returnTypeContextualTupleTyping1.ts, 3, 43)) + diff --git a/tests/baselines/reference/returnTypeContextualTupleTyping1.types b/tests/baselines/reference/returnTypeContextualTupleTyping1.types new file mode 100644 index 0000000000000..632700348c81e --- /dev/null +++ b/tests/baselines/reference/returnTypeContextualTupleTyping1.types @@ -0,0 +1,168 @@ +//// [tests/cases/compiler/returnTypeContextualTupleTyping1.ts] //// + +=== returnTypeContextualTupleTyping1.ts === +// https://github.com/microsoft/TypeScript/issues/62071 + +declare function getNum(): Promise; +>getNum : () => Promise +> : ^^^^^^ + +declare function getStr(): Promise; +>getStr : () => Promise +> : ^^^^^^ + +declare function useTuple(tuple: [number, string]): void; +>useTuple : (tuple: [number, string]) => void +> : ^ ^^ ^^^^^ +>tuple : [number, string] +> : ^^^^^^^^^^^^^^^^ + +const p = Promise.resolve([]) +>p : Promise +> : ^^^^^^^^^^^^^ +>Promise.resolve([]) .then(() => Promise.all([getNum(), getStr()])) .then(useTuple) : Promise +> : ^^^^^^^^^^^^^ +>Promise.resolve([]) .then(() => Promise.all([getNum(), getStr()])) .then : (onfulfilled?: ((value: [number, string]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +> : ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Promise.resolve([]) .then(() => Promise.all([getNum(), getStr()])) : Promise<[number, string]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>Promise.resolve([]) .then : (onfulfilled?: ((value: never[]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +> : ^ ^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Promise.resolve([]) : Promise +> : ^^^^^^^^^^^^^^^^ +>Promise.resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } +> : ^^^^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ +>Promise : PromiseConstructor +> : ^^^^^^^^^^^^^^^^^^ +>resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } +> : ^^^^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ +>[] : never[] +> : ^^^^^^^ + + .then(() => Promise.all([getNum(), getStr()])) +>then : (onfulfilled?: ((value: never[]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +> : ^ ^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => Promise.all([getNum(), getStr()]) : () => Promise<[number, string]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Promise.all([getNum(), getStr()]) : Promise<[number, string]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>Promise.all : { (values: Iterable>): Promise[]>; (values: T): Promise<{ -readonly [P in keyof T]: Awaited; }>; } +> : ^^^ ^^ ^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^^ ^^^ +>Promise : PromiseConstructor +> : ^^^^^^^^^^^^^^^^^^ +>all : { (values: Iterable>): Promise[]>; (values: T): Promise<{ -readonly [P in keyof T]: Awaited; }>; } +> : ^^^ ^^ ^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^^ ^^^ +>[getNum(), getStr()] : [Promise, Promise] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>getNum() : Promise +> : ^^^^^^^^^^^^^^^ +>getNum : () => Promise +> : ^^^^^^ +>getStr() : Promise +> : ^^^^^^^^^^^^^^^ +>getStr : () => Promise +> : ^^^^^^ + + .then(useTuple); // ok +>then : (onfulfilled?: ((value: [number, string]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +> : ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>useTuple : (tuple: [number, string]) => void +> : ^ ^^ ^^^^^ + +// same as above but without relying as much on builtin libs +interface MyPromise { + then< +>then : (onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null) => MyPromise +> : ^ ^^^^^^ ^^^^^^^^^^ ^^^ ^^ ^^^ ^^^^^ + + TResult1 = T, // outer type parameter used as default here is important for the test + TResult2 = never, + >( + onfulfilled?: +>onfulfilled : ((value: T) => TResult1 | PromiseLike) | null | undefined +> : ^^ ^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^ + + | ((value: T) => TResult1 | PromiseLike) +>value : T +> : ^ + + | undefined + | null, + onrejected?: +>onrejected : ((reason: any) => TResult2 | PromiseLike) | null | undefined +> : ^^ ^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^ + + | ((reason: any) => TResult2 | PromiseLike) +>reason : any + + | undefined + | null, + ): MyPromise; +} + +declare function resolve(value: T): MyPromise>; +>resolve : { (value: T): MyPromise>; (value: T_1 | PromiseLike): MyPromise>; } +> : ^^^ ^^ ^^ ^^^ ^^^^^^^^ ^^ ^^^ ^^^ +>value : T +> : ^ + +declare function resolve(value: T | PromiseLike): MyPromise>; +>resolve : { (value: T_1): MyPromise>; (value: T | PromiseLike): MyPromise>; } +> : ^^^^^^^^ ^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ +>value : T | PromiseLike +> : ^^^^^^^^^^^^^^^^^^ + +declare function all( +>all : (values: T) => MyPromise<{ -readonly [P in keyof T]: Awaited; }> +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ + + values: T, +>values : T +> : ^ + +): MyPromise<{ -readonly [P in keyof T]: Awaited }>; + +const p2 = resolve([]) +>p2 : MyPromise +> : ^^^^^^^^^^^^^^^ +>resolve([]) .then(() => all([getNum(), getStr()])) .then(useTuple) : MyPromise +> : ^^^^^^^^^^^^^^^ +>resolve([]) .then(() => all([getNum(), getStr()])) .then : (onfulfilled?: ((value: [number, string]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => MyPromise +> : ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>resolve([]) .then(() => all([getNum(), getStr()])) : MyPromise<[number, string]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>resolve([]) .then : (onfulfilled?: ((value: never[]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => MyPromise +> : ^ ^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>resolve([]) : MyPromise +> : ^^^^^^^^^^^^^^^^^^ +>resolve : { (value: T): MyPromise>; (value: T | PromiseLike): MyPromise>; } +> : ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ +>[] : never[] +> : ^^^^^^^ + + .then(() => all([getNum(), getStr()])) +>then : (onfulfilled?: ((value: never[]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => MyPromise +> : ^ ^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => all([getNum(), getStr()]) : () => MyPromise<[number, string]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>all([getNum(), getStr()]) : MyPromise<[number, string]> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>all : (values: T) => MyPromise<{ -readonly [P in keyof T]: Awaited; }> +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>[getNum(), getStr()] : [Promise, Promise] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>getNum() : Promise +> : ^^^^^^^^^^^^^^^ +>getNum : () => Promise +> : ^^^^^^ +>getStr() : Promise +> : ^^^^^^^^^^^^^^^ +>getStr : () => Promise +> : ^^^^^^ + + .then(useTuple); // ok +>then : (onfulfilled?: ((value: [number, string]) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => MyPromise +> : ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>useTuple : (tuple: [number, string]) => void +> : ^ ^^ ^^^^^ + diff --git a/tests/cases/compiler/returnTypeContextualTupleTyping1.ts b/tests/cases/compiler/returnTypeContextualTupleTyping1.ts new file mode 100644 index 0000000000000..f15a0a856707f --- /dev/null +++ b/tests/cases/compiler/returnTypeContextualTupleTyping1.ts @@ -0,0 +1,41 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62071 + +declare function getNum(): Promise; +declare function getStr(): Promise; +declare function useTuple(tuple: [number, string]): void; + +const p = Promise.resolve([]) + .then(() => Promise.all([getNum(), getStr()])) + .then(useTuple); // ok + +// same as above but without relying as much on builtin libs +interface MyPromise { + then< + TResult1 = T, // outer type parameter used as default here is important for the test + TResult2 = never, + >( + onfulfilled?: + | ((value: T) => TResult1 | PromiseLike) + | undefined + | null, + onrejected?: + | ((reason: any) => TResult2 | PromiseLike) + | undefined + | null, + ): MyPromise; +} + +declare function resolve(value: T): MyPromise>; +declare function resolve(value: T | PromiseLike): MyPromise>; + +declare function all( + values: T, +): MyPromise<{ -readonly [P in keyof T]: Awaited }>; + +const p2 = resolve([]) + .then(() => all([getNum(), getStr()])) + .then(useTuple); // ok