Skip to content

Only resolve source return type when actually needed during inference #58650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 28, 2024
Merged
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
14 changes: 9 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25172,13 +25172,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function applyToReturnTypes(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) {
const sourceTypePredicate = getTypePredicateOfSignature(source);
const targetTypePredicate = getTypePredicateOfSignature(target);
if (sourceTypePredicate && targetTypePredicate && typePredicateKindsMatch(sourceTypePredicate, targetTypePredicate) && sourceTypePredicate.type && targetTypePredicate.type) {
callback(sourceTypePredicate.type, targetTypePredicate.type);
if (targetTypePredicate) {
const sourceTypePredicate = getTypePredicateOfSignature(source);
if (sourceTypePredicate && typePredicateKindsMatch(sourceTypePredicate, targetTypePredicate) && sourceTypePredicate.type && targetTypePredicate.type) {
callback(sourceTypePredicate.type, targetTypePredicate.type);
return;
}
}
else {
callback(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
const targetReturnType = getReturnTypeOfSignature(target);
if (couldContainTypeVariables(targetReturnType)) {
callback(getReturnTypeOfSignature(source), targetReturnType);
}
}

Expand Down
18 changes: 11 additions & 7 deletions tests/baselines/reference/circularReferenceInReturnType.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
circularReferenceInReturnType.ts(3,7): error TS7022: 'res1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
circularReferenceInReturnType.ts(3,18): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
circularReferenceInReturnType.ts(9,7): error TS7022: 'res3' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
circularReferenceInReturnType.ts(9,20): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.


==== circularReferenceInReturnType.ts (4 errors) ====
==== circularReferenceInReturnType.ts (2 errors) ====
// inference fails for res1 and res2, but ideally should not
declare function fn1<T>(cb: () => T): string;
const res1 = fn1(() => res1);
Expand All @@ -18,8 +16,14 @@ circularReferenceInReturnType.ts(9,20): error TS7024: Function implicitly has re

declare function fn3<T>(): <T2>(cb: (arg: T2) => any) => (a: T) => void;
const res3 = fn3()(() => res3);
~~~~
!!! error TS7022: 'res3' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
~~~~~~~~~~
!!! error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.

// https://github.com/microsoft/TypeScript/issues/58616

function foo(arg: Parameters<typeof bar>[0]) {
return arg;
}

function bar(arg: string) {
return foo(arg);
}

21 changes: 21 additions & 0 deletions tests/baselines/reference/circularReferenceInReturnType.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,24 @@ const res3 = fn3()(() => res3);
>fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 5, 31))
>res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 8, 5))

// https://github.com/microsoft/TypeScript/issues/58616

function foo(arg: Parameters<typeof bar>[0]) {
>foo : Symbol(foo, Decl(circularReferenceInReturnType.ts, 8, 31))
>arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 12, 13))
>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --))
>bar : Symbol(bar, Decl(circularReferenceInReturnType.ts, 14, 1))

return arg;
>arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 12, 13))
}

function bar(arg: string) {
>bar : Symbol(bar, Decl(circularReferenceInReturnType.ts, 14, 1))
>arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 16, 13))

return foo(arg);
>foo : Symbol(foo, Decl(circularReferenceInReturnType.ts, 8, 31))
>arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 16, 13))
}

42 changes: 36 additions & 6 deletions tests/baselines/reference/circularReferenceInReturnType.types
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,46 @@ declare function fn3<T>(): <T2>(cb: (arg: T2) => any) => (a: T) => void;
> : ^

const res3 = fn3()(() => res3);
>res3 : any
> : ^^^
>res3 : (a: unknown) => void
> : ^ ^^^^^^^^^^^^^^
>fn3()(() => res3) : (a: unknown) => void
> : ^ ^^^^^^^^^^^^^^
>fn3() : <T2>(cb: (arg: T2) => any) => (a: unknown) => void
> : ^^^^^ ^^^ ^^^^^^^^^ ^^^^^ ^^^^^^^
>fn3 : <T>() => <T2>(cb: (arg: T2) => any) => (a: T) => void
> : ^ ^^^^^^^
>() => res3 : () => any
> : ^^^^^^^^^
>res3 : any
> : ^^^
>() => res3 : () => (a: unknown) => void
> : ^^^^^^^ ^^^^^^^^^^^^^^
>res3 : (a: unknown) => void
> : ^ ^^^^^^^^^^^^^^

// https://github.com/microsoft/TypeScript/issues/58616

function foo(arg: Parameters<typeof bar>[0]) {
>foo : (arg: Parameters<typeof bar>[0]) => string
> : ^ ^^ ^^^^^^^^^^^
>arg : string
> : ^^^^^^
>bar : (arg: string) => string
> : ^ ^^ ^^^^^^^^^^^

return arg;
>arg : string
> : ^^^^^^
}

function bar(arg: string) {
>bar : (arg: string) => string
> : ^ ^^ ^^^^^^^^^^^
>arg : string
> : ^^^^^^

return foo(arg);
>foo(arg) : string
> : ^^^^^^
>foo : (arg: Parameters<typeof bar>[0]) => string
> : ^ ^^ ^^^^^^^^^^^
>arg : string
> : ^^^^^^
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 10,000
Type Count: 50,000
Instantiation count: 250,000
Instantiation count: 100,000
Copy link
Member

Choose a reason for hiding this comment

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

Nice!

Symbol count: 100,000

=== complex.ts ===
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
flatArrayNoExcessiveStackDepth.ts(20,5): error TS2322: Type 'FlatArray<Arr, any>' is not assignable to type 'FlatArray<Arr, D>'.
Type 'Arr' is not assignable to type 'FlatArray<Arr, D>'.
Type 'Arr' is not assignable to type '(Arr extends readonly (infer InnerArr)[] ? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][D]> : Arr) & Arr'.
Type 'Arr' is not assignable to type 'Arr & (Arr extends readonly (infer InnerArr)[] ? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][D]> : Arr)'.
Type 'Arr' is not assignable to type 'Arr extends readonly (infer InnerArr)[] ? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][D]> : Arr'.


Expand Down Expand Up @@ -28,7 +28,7 @@ flatArrayNoExcessiveStackDepth.ts(20,5): error TS2322: Type 'FlatArray<Arr, any>
~
!!! error TS2322: Type 'FlatArray<Arr, any>' is not assignable to type 'FlatArray<Arr, D>'.
!!! error TS2322: Type 'Arr' is not assignable to type 'FlatArray<Arr, D>'.
!!! error TS2322: Type 'Arr' is not assignable to type '(Arr extends readonly (infer InnerArr)[] ? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][D]> : Arr) & Arr'.
!!! error TS2322: Type 'Arr' is not assignable to type 'Arr & (Arr extends readonly (infer InnerArr)[] ? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][D]> : Arr)'.
Copy link
Member

Choose a reason for hiding this comment

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

I'm guessing that because a return type was not eagerly calculated at some step, one of these types was created later than the other.

Copy link
Member

@DanielRosenwasser DanielRosenwasser May 24, 2024

Choose a reason for hiding this comment

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

Though we don't sort type IDs in intersections, so that's not it.

Copy link
Member Author

@ahejlsberg ahejlsberg May 24, 2024

Choose a reason for hiding this comment

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

I'm sure it's a case of a distributing over a union type and turning it into an intersection type in a write-position. We do that in several places in type simplification and type relations. For example, an indexed access type T[K], where K is a union type, occurring in a write-position.

!!! error TS2322: Type 'Arr' is not assignable to type 'Arr extends readonly (infer InnerArr)[] ? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][D]> : Arr'.
}

10 changes: 10 additions & 0 deletions tests/cases/compiler/circularReferenceInReturnType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ const res2 = fn2()(() => res2);

declare function fn3<T>(): <T2>(cb: (arg: T2) => any) => (a: T) => void;
const res3 = fn3()(() => res3);

// https://github.com/microsoft/TypeScript/issues/58616

function foo(arg: Parameters<typeof bar>[0]) {
return arg;
}

function bar(arg: string) {
return foo(arg);
}