microsoft/TypeScript

Contextual typing is wrong in nested function calls.

Closed this issue ยท 3 comments

๐Ÿ”Ž Search Terms

I have run into the following issue while working on a schema lib.
https://tsplay.dev/w1eekw
The type of varaible d is any for some reason, at line 78.
On the discord it was suggested that this is a compiler bug.
https://discord.com/channels/508357248330760243/1428318052205854720/1432271574689517660

๐Ÿ•— Version & Regression Information

  • This is the behavior in every version I tried: 5.9.3, 5.8.3, 5.7.3

โฏ Playground Link

https://tsplay.dev/w1eekw

๐Ÿ’ป Code

// Your code here

๐Ÿ™ Actual behavior

The type of the d param in the lambda is any.

๐Ÿ™‚ Expected behavior

It should be of type User

Additional information about the issue

No response

Please reduce this to something minimal

The repro ain't that bad if we just drop the irrelevant runtime code: TS playground

It is a design limitation. It boils down to what is described in this comment:

        // When a call to a generic function is an argument to an outer call to a generic function for which
        // inference is in process, we have a choice to make. If the inner call relies on inferences made from
        // its contextual type to its return type, deferring the inner call processing allows the best possible
        // contextual type to accumulate. But if the outer call relies on inferences made from the return type of
        // the inner call, the inner call should be processed early. There's no sure way to know which choice is
        // right (only a full unification algorithm can determine that), so we resort to the following heuristic:
        // If no type arguments are specified in the inner call and at least one call signature is generic and
        // returns a function type, we choose to defer processing. This narrowly permits function composition
        // operators to flow inferences through return types, but otherwise processes calls right away. We
        // use the resolvingSignature singleton to indicate that we deferred processing. This result will be
        // propagated out and eventually turned into silentNeverType (a type that is assignable to anything and
        // from which we never make inferences).
        if (checkMode & CheckMode.SkipGenericFunctions && !node.typeArguments && callSignatures.some(isGenericFunctionReturningFunction)) {
            skippedGenericFunction(node, checkMode);
            return resolvingSignature;
        }

We can somewhat easily verify this is relevant because the issue gets "fixed" if we add a dummy signature to MTypeBase (TS playground):

interface MTypeBase<TSource, TValue> {
  id: string;
  getter: Getter<TSource, TValue>;
+(...args: never): unknown
}

This makes the code defer resolving this inner call (as described in the comment)

This issue has been marked as "Design Limitation" and has seen no recent activity. It has been automatically closed for house-keeping purposes.