Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -29795,7 +29795,8 @@ func (c *Checker) getContextualReturnType(functionDecl *ast.Node, contextFlags C
// Otherwise, if the containing function is contextually typed by a function type with exactly one call signature
// and that call signature is non-generic, return statements are contextually typed by the return type of the signature
signature := c.getContextualSignatureForFunctionLikeDeclaration(functionDecl)
if signature != nil && !c.isResolvingReturnTypeOfSignature(signature) {
// A function's own inferred return type must not become contextual evidence for its body.
if signature != nil && signature != c.getSignatureFromDeclaration(functionDecl) && !c.isResolvingReturnTypeOfSignature(signature) {
returnType := c.getReturnTypeOfSignature(signature)
functionFlags := ast.GetFunctionFlags(functionDecl)
if functionFlags&ast.FunctionFlagsGenerator != 0 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestQuickInfoGenericDefaultInReentrantCallResolution(t *testing.T) {
t.Parallel()
const content = `// @target: es2015
// @strict: true
declare function fetcher<D>(cfg: unknown): Promise<D>;
type Routes<R> = R extends "x" ? { IN: { v: number }; OUT: string } : never;
function executeFetch<R extends string, T = Routes<R>>(
route: R,
body?: T extends Record<"IN", any> ? T["IN"] : never,
) {
return fetcher<T extends Record<"OUT", any> ? T["OUT"] : T>({ route, body });
}
declare function useCallback<T extends Function>(cb: T, deps: unknown[]): T;
export const /*callback*/cb = useCallback((data: { v: number }) => {
return executeFetch("x", /*argument*/data);
}, []);
export const check: Promise<string> = cb({ v: 1 });`
for _, first := range []string{"callback", "argument", "diagnostics"} {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In here, I just make sure the cached signatures stay the same regardless of the order of the LSP requests

t.Run(first, func(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
callbackInfo := "const cb: (data: {\n v: number;\n}) => Promise<string>"
argumentInfo := "(parameter) data: {\n v: number;\n}"
switch first {
case "callback":
f.VerifyQuickInfoAt(t, "callback", callbackInfo, "")
case "argument":
f.VerifyQuickInfoAt(t, "argument", argumentInfo, "")
case "diagnostics":
f.VerifyNoErrors(t)
}
f.VerifyQuickInfoAt(t, "callback", callbackInfo, "")
f.VerifyQuickInfoAt(t, "argument", argumentInfo, "")
f.VerifyNoErrors(t)
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//// [tests/cases/compiler/genericDefaultInReentrantCallResolution.ts] ////

=== genericDefaultInReentrantCallResolution.ts ===
// https://github.com/microsoft/TypeScript/issues/63949

declare function fetcher<D>(cfg: unknown): Promise<D>;
>fetcher : Symbol(fetcher, Decl(genericDefaultInReentrantCallResolution.ts, 0, 0))
>D : Symbol(D, Decl(genericDefaultInReentrantCallResolution.ts, 2, 25))
>cfg : Symbol(cfg, Decl(genericDefaultInReentrantCallResolution.ts, 2, 28))
>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, --, --))
>D : Symbol(D, Decl(genericDefaultInReentrantCallResolution.ts, 2, 25))

type Routes<R> = R extends "x" ? { IN: { v: number }; OUT: string } : never;
>Routes : Symbol(Routes, Decl(genericDefaultInReentrantCallResolution.ts, 2, 54))
>R : Symbol(R, Decl(genericDefaultInReentrantCallResolution.ts, 4, 12))
>R : Symbol(R, Decl(genericDefaultInReentrantCallResolution.ts, 4, 12))
>IN : Symbol(IN, Decl(genericDefaultInReentrantCallResolution.ts, 4, 34))
>v : Symbol(v, Decl(genericDefaultInReentrantCallResolution.ts, 4, 40))
>OUT : Symbol(OUT, Decl(genericDefaultInReentrantCallResolution.ts, 4, 53))

function executeFetch<R extends string, T = Routes<R>>(
>executeFetch : Symbol(executeFetch, Decl(genericDefaultInReentrantCallResolution.ts, 4, 76))
>R : Symbol(R, Decl(genericDefaultInReentrantCallResolution.ts, 6, 22))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 6, 39))
>Routes : Symbol(Routes, Decl(genericDefaultInReentrantCallResolution.ts, 2, 54))
>R : Symbol(R, Decl(genericDefaultInReentrantCallResolution.ts, 6, 22))

route: R,
>route : Symbol(route, Decl(genericDefaultInReentrantCallResolution.ts, 6, 55))
>R : Symbol(R, Decl(genericDefaultInReentrantCallResolution.ts, 6, 22))

body?: T extends Record<"IN", any> ? T["IN"] : never,
>body : Symbol(body, Decl(genericDefaultInReentrantCallResolution.ts, 7, 13))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 6, 39))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 6, 39))

) {
return fetcher<T extends Record<"OUT", any> ? T["OUT"] : T>({ route, body });
>fetcher : Symbol(fetcher, Decl(genericDefaultInReentrantCallResolution.ts, 0, 0))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 6, 39))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 6, 39))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 6, 39))
>route : Symbol(route, Decl(genericDefaultInReentrantCallResolution.ts, 10, 65))
>body : Symbol(body, Decl(genericDefaultInReentrantCallResolution.ts, 10, 72))
}

declare function useCallback<T extends Function>(cb: T, deps: unknown[]): T;
>useCallback : Symbol(useCallback, Decl(genericDefaultInReentrantCallResolution.ts, 11, 1))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 13, 29))
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>cb : Symbol(cb, Decl(genericDefaultInReentrantCallResolution.ts, 13, 49))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 13, 29))
>deps : Symbol(deps, Decl(genericDefaultInReentrantCallResolution.ts, 13, 55))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 13, 29))

export const cb = useCallback((data: { v: number }) => {
>cb : Symbol(cb, Decl(genericDefaultInReentrantCallResolution.ts, 15, 12))
>useCallback : Symbol(useCallback, Decl(genericDefaultInReentrantCallResolution.ts, 11, 1))
>data : Symbol(data, Decl(genericDefaultInReentrantCallResolution.ts, 15, 31))
>v : Symbol(v, Decl(genericDefaultInReentrantCallResolution.ts, 15, 38))

return executeFetch("x", data);
>executeFetch : Symbol(executeFetch, Decl(genericDefaultInReentrantCallResolution.ts, 4, 76))
>data : Symbol(data, Decl(genericDefaultInReentrantCallResolution.ts, 15, 31))

}, []);

export const check: Promise<string> = cb({ v: 1 });
>check : Symbol(check, Decl(genericDefaultInReentrantCallResolution.ts, 19, 12))
>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, --, --))
>cb : Symbol(cb, Decl(genericDefaultInReentrantCallResolution.ts, 15, 12))
>v : Symbol(v, Decl(genericDefaultInReentrantCallResolution.ts, 19, 42))

// https://github.com/microsoft/TypeScript/issues/63949#issuecomment-5400383233

declare function request<T = { input: { value: number }; output: string }>(
>request : Symbol(request, Decl(genericDefaultInReentrantCallResolution.ts, 19, 51))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 23, 25))
>input : Symbol(input, Decl(genericDefaultInReentrantCallResolution.ts, 23, 30))
>value : Symbol(value, Decl(genericDefaultInReentrantCallResolution.ts, 23, 39))
>output : Symbol(output, Decl(genericDefaultInReentrantCallResolution.ts, 23, 56))

body?: T extends { input: unknown } ? T["input"] : never,
>body : Symbol(body, Decl(genericDefaultInReentrantCallResolution.ts, 23, 75))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 23, 25))
>input : Symbol(input, Decl(genericDefaultInReentrantCallResolution.ts, 24, 22))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 23, 25))

): T extends { output: unknown } ? T["output"] : T;
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 23, 25))
>output : Symbol(output, Decl(genericDefaultInReentrantCallResolution.ts, 25, 14))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 23, 25))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 23, 25))

declare function identity<T>(callback: T): T;
>identity : Symbol(identity, Decl(genericDefaultInReentrantCallResolution.ts, 25, 51))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 27, 26))
>callback : Symbol(callback, Decl(genericDefaultInReentrantCallResolution.ts, 27, 29))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 27, 26))
>T : Symbol(T, Decl(genericDefaultInReentrantCallResolution.ts, 27, 26))

identity((data: { value: number }) =>
>identity : Symbol(identity, Decl(genericDefaultInReentrantCallResolution.ts, 25, 51))
>data : Symbol(data, Decl(genericDefaultInReentrantCallResolution.ts, 29, 10))
>value : Symbol(value, Decl(genericDefaultInReentrantCallResolution.ts, 29, 17))

request(data),
>request : Symbol(request, Decl(genericDefaultInReentrantCallResolution.ts, 19, 51))
>data : Symbol(data, Decl(genericDefaultInReentrantCallResolution.ts, 29, 10))

);

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//// [tests/cases/compiler/genericDefaultInReentrantCallResolution.ts] ////

=== genericDefaultInReentrantCallResolution.ts ===
// https://github.com/microsoft/TypeScript/issues/63949

declare function fetcher<D>(cfg: unknown): Promise<D>;
>fetcher : <D>(cfg: unknown) => Promise<D>
>cfg : unknown

type Routes<R> = R extends "x" ? { IN: { v: number }; OUT: string } : never;
>Routes : Routes<R>
>IN : { v: number; }
>v : number
>OUT : string

function executeFetch<R extends string, T = Routes<R>>(
>executeFetch : <R extends string, T = Routes<R>>(route: R, body?: T extends Record<"IN", any> ? T["IN"] : never) => Promise<T extends Record<"OUT", any> ? T["OUT"] : T>

route: R,
>route : R

body?: T extends Record<"IN", any> ? T["IN"] : never,
>body : (T extends Record<"IN", any> ? T["IN"] : never) | undefined

) {
return fetcher<T extends Record<"OUT", any> ? T["OUT"] : T>({ route, body });
>fetcher<T extends Record<"OUT", any> ? T["OUT"] : T>({ route, body }) : Promise<T extends Record<"OUT", any> ? T["OUT"] : T>
>fetcher : <D>(cfg: unknown) => Promise<D>
>{ route, body } : { route: R; body: (T extends Record<"IN", any> ? T["IN"] : never) | undefined; }
>route : R
>body : (T extends Record<"IN", any> ? T["IN"] : never) | undefined
}

declare function useCallback<T extends Function>(cb: T, deps: unknown[]): T;
>useCallback : <T extends Function>(cb: T, deps: unknown[]) => T
>cb : T
>deps : unknown[]

export const cb = useCallback((data: { v: number }) => {
>cb : (data: { v: number; }) => Promise<string>
>useCallback((data: { v: number }) => { return executeFetch("x", data);}, []) : (data: { v: number; }) => Promise<string>
>useCallback : <T extends Function>(cb: T, deps: unknown[]) => T
>(data: { v: number }) => { return executeFetch("x", data);} : (data: { v: number; }) => Promise<string>
>data : { v: number; }
>v : number

return executeFetch("x", data);
>executeFetch("x", data) : Promise<string>
>executeFetch : <R extends string, T = Routes<R>>(route: R, body?: T extends Record<"IN", any> ? T["IN"] : never) => Promise<T extends Record<"OUT", any> ? T["OUT"] : T>
>"x" : "x"
>data : { v: number; }

}, []);
>[] : never[]

export const check: Promise<string> = cb({ v: 1 });
>check : Promise<string>
>cb({ v: 1 }) : Promise<string>
>cb : (data: { v: number; }) => Promise<string>
>{ v: 1 } : { v: number; }
>v : number
>1 : 1

// https://github.com/microsoft/TypeScript/issues/63949#issuecomment-5400383233

declare function request<T = { input: { value: number }; output: string }>(
>request : <T = { input: { value: number; }; output: string; }>(body?: T extends { input: unknown; } ? T["input"] : never) => T extends { output: unknown; } ? T["output"] : T
>input : { value: number; }
>value : number
>output : string

body?: T extends { input: unknown } ? T["input"] : never,
>body : (T extends { input: unknown; } ? T["input"] : never) | undefined
>input : unknown

): T extends { output: unknown } ? T["output"] : T;
>output : unknown

declare function identity<T>(callback: T): T;
>identity : <T>(callback: T) => T
>callback : T

identity((data: { value: number }) =>
>identity((data: { value: number }) => request(data),) : (data: { value: number; }) => string
>identity : <T>(callback: T) => T
>(data: { value: number }) => request(data) : (data: { value: number; }) => string
>data : { value: number; }
>value : number

request(data),
>request(data) : string
>request : <T = { input: { value: number; }; output: string; }>(body?: T extends { input: unknown; } ? T["input"] : never) => T extends { output: unknown; } ? T["output"] : T
>data : { value: number; }

);

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
reentrantCallDoesNotReuseInapplicableCachedSignature.ts(6,20): error TS2345: Argument of type '(a: string) => string' is not assignable to parameter of type '(a: string) => number'.
Type 'string' is not assignable to type 'number'.


==== reentrantCallDoesNotReuseInapplicableCachedSignature.ts (1 errors) ====
declare const example: (f: (a: string) => number) => string;

const f = (a: string) => g();

const g = () => {
return example(f);
~
!!! error TS2345: Argument of type '(a: string) => string' is not assignable to parameter of type '(a: string) => number'.
!!! error TS2345: Type 'string' is not assignable to type 'number'.
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//// [tests/cases/compiler/reentrantCallDoesNotReuseInapplicableCachedSignature.ts] ////

=== reentrantCallDoesNotReuseInapplicableCachedSignature.ts ===
declare const example: (f: (a: string) => number) => string;
>example : Symbol(example, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 0, 13))
>f : Symbol(f, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 0, 24))
>a : Symbol(a, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 0, 28))

const f = (a: string) => g();
>f : Symbol(f, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 2, 5))
>a : Symbol(a, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 2, 11))
>g : Symbol(g, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 4, 5))

const g = () => {
>g : Symbol(g, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 4, 5))

return example(f);
>example : Symbol(example, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 0, 13))
>f : Symbol(f, Decl(reentrantCallDoesNotReuseInapplicableCachedSignature.ts, 2, 5))

};

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//// [tests/cases/compiler/reentrantCallDoesNotReuseInapplicableCachedSignature.ts] ////

=== reentrantCallDoesNotReuseInapplicableCachedSignature.ts ===
declare const example: (f: (a: string) => number) => string;
>example : (f: (a: string) => number) => string
>f : (a: string) => number
>a : string

const f = (a: string) => g();
>f : (a: string) => string
>(a: string) => g() : (a: string) => string
>a : string
>g() : string
>g : () => string

const g = () => {
>g : () => string
>() => { return example(f);} : () => string

return example(f);
>example(f) : string
>example : (f: (a: string) => number) => string
>f : (a: string) => string

};

Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ const Component = <T extends Animations>({
>func : () => { a: true; }

return {
>{ a: true, } : { a: true; }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think those are correct, the inferred signature here includes boolean and then that object became checked with that as its contextual part so a true literal became here contextually-typed by a boolean and that makes it to retain the true type (instead of widening to boolean)

>{ a: true, } : { a: boolean; }

a: true,
>a : true
>a : boolean
>true : true

};
Expand Down Expand Up @@ -192,10 +192,10 @@ const Component = <T extends Animations>({
>() => { return { a: true, }; } : () => { a: true; }

return {
>{ a: true, } : { a: true; }
>{ a: true, } : { a: boolean; }

a: true,
>a : true
>a : boolean
>true : true

};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ const Component = registerComponent('test-component', {
>parse : () => boolean[]

return [true];
>[true] : true[]
>[true] : boolean[]
>true : true
}
},
Expand Down
Loading