Skip to content
Open
24 changes: 17 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37938,14 +37938,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : fallbackType;
}

function inferFromAnnotatedParameters(signature: Signature, context: Signature, inferenceContext: InferenceContext) {
const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
for (let i = 0; i < len; i++) {
const declaration = signature.parameters[i].valueDeclaration as ParameterDeclaration;
function inferFromAnnotatedParameters(node: FunctionExpression | ArrowFunction | MethodDeclaration, contextualSignature: Signature, inferenceContext: InferenceContext) {
if (!node.parameters.length) {
return;
}
const len = node.parameters.length - (last(node.parameters).dotDotDotToken ? 1 : 0);
const thisParameterOffset = first(node.parameters).symbol.escapedName === InternalSymbolName.This ? 1 : 0;
for (let i = thisParameterOffset; i < len; i++) {
const declaration = node.parameters[i];
const typeNode = getEffectiveTypeAnnotationNode(declaration);
if (typeNode) {
const source = addOptionality(getTypeFromTypeNode(typeNode), /*isProperty*/ false, isOptionalDeclaration(declaration));
const target = getTypeAtPosition(context, i);
const target = getTypeAtPosition(contextualSignature, i - thisParameterOffset);
inferTypes(inferenceContext.inferences, source, target);
}
}
Expand Down Expand Up @@ -38913,6 +38917,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return links.contextFreeType = returnOnlyType;
}
}
if (checkMode & CheckMode.Inferential) {
const contextualSignature = getContextualSignature(node);
if (contextualSignature) {
inferFromAnnotatedParameters(node, contextualSignature, getInferenceContext(node)!);
}
}
Comment on lines 39542 to 39547
Copy link
Member

Choose a reason for hiding this comment

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

Is it not a little weird for getInferenceContext and inferFromAnnotatedParameters to get called here? I'm not finding other examples where we're doing this sort of thing outside of a function intended to contextually check (which this function is not, contextuallyCheckFunctionExpressionOrObjectLiteralMethod is).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hm, this might be the first time this is called like it but the placement of those calls here is what fixes the bug conceptually - as in, for the added test cases it's important to infer before contextually checking those functions

Contextual check of functions can fix "requested" inference type parameters and that creates an ordering issue for #56459 as the parameter type "request" can happen before inferFromAnnotatedParameters even has a chance to be called on the other function that comes later in the source code.

And the similar problem happens when it comes to #60047 . The conditional type asks for the inferred type of the type parameter and since by that time the inferFromAnnotatedParameters was not called it resolves to its constraint. Then the arguments fail to be typechecked against current parameter types in getSignatureApplicabilityError so the algorithm returns, never reaching the second inference pass that starts to include context-sensitive parameters.

So if those issues are meant to be fixed it's absolutely necessary to call inferFromAnnotatedParameters within the first inference pass and that doesn't check context-sensitive functions, that's deferred to the second inference pass. I don't see how to even attempt to fix this in any other way given the known limitations around context-sensitive expressions and their associated implementation details

return anyFunctionType;
}

Expand Down Expand Up @@ -38946,7 +38956,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const inferenceContext = getInferenceContext(node);
let instantiatedContextualSignature: Signature | undefined;
if (checkMode && checkMode & CheckMode.Inferential) {
inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!);
inferFromAnnotatedParameters(node, contextualSignature, inferenceContext!);
const restType = getEffectiveRestType(contextualSignature);
if (restType && restType.flags & TypeFlags.TypeParameter) {
instantiatedContextualSignature = instantiateSignature(contextualSignature, inferenceContext!.nonFixingMapper);
Expand All @@ -38964,7 +38974,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
else if (contextualSignature && !node.typeParameters && contextualSignature.parameters.length > node.parameters.length) {
const inferenceContext = getInferenceContext(node);
if (checkMode && checkMode & CheckMode.Inferential) {
inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!);
inferFromAnnotatedParameters(node, contextualSignature, inferenceContext!);
}
}
if (contextualSignature && !getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//// [tests/cases/compiler/contextSensitiveAnnotatedParametersInference1.ts] ////

=== contextSensitiveAnnotatedParametersInference1.ts ===
declare function test<T, A, B>(obj: {
>test : Symbol(test, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 0))
>T : Symbol(T, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 22))
>A : Symbol(A, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 24))
>B : Symbol(B, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 27))
>obj : Symbol(obj, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 31))

ctx: T;
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 37))
>T : Symbol(T, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 22))

a: (a: A, ctx: T) => void;
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 1, 9))
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 2, 6))
>A : Symbol(A, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 24))
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 2, 11))
>T : Symbol(T, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 22))

b: (b: B, ctx: T, a: A) => void;
>b : Symbol(b, Decl(contextSensitiveAnnotatedParametersInference1.ts, 2, 28))
>b : Symbol(b, Decl(contextSensitiveAnnotatedParametersInference1.ts, 3, 6))
>B : Symbol(B, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 27))
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 3, 11))
>T : Symbol(T, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 22))
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 3, 19))
>A : Symbol(A, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 24))

}): void;

test({
>test : Symbol(test, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 0))

ctx: 'foo',
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 6, 6))

a: (a: string, ctx) => {},
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 7, 13))
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 8, 6))
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 8, 16))

b: (b: string, ctx, a) => {},
>b : Symbol(b, Decl(contextSensitiveAnnotatedParametersInference1.ts, 8, 28))
>b : Symbol(b, Decl(contextSensitiveAnnotatedParametersInference1.ts, 9, 6))
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 9, 16))
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 9, 21))

});

test({
>test : Symbol(test, Decl(contextSensitiveAnnotatedParametersInference1.ts, 0, 0))

ctx: 'foo',
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 12, 6))

b: (b: string, ctx, a) => {},
>b : Symbol(b, Decl(contextSensitiveAnnotatedParametersInference1.ts, 13, 13))
>b : Symbol(b, Decl(contextSensitiveAnnotatedParametersInference1.ts, 14, 6))
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 14, 16))
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 14, 21))

a: (a: string, ctx) => {},
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 14, 31))
>a : Symbol(a, Decl(contextSensitiveAnnotatedParametersInference1.ts, 15, 6))
>ctx : Symbol(ctx, Decl(contextSensitiveAnnotatedParametersInference1.ts, 15, 16))

});

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

=== contextSensitiveAnnotatedParametersInference1.ts ===
declare function test<T, A, B>(obj: {
>test : <T, A, B>(obj: { ctx: T; a: (a: A, ctx: T) => void; b: (b: B, ctx: T, a: A) => void; }) => void
> : ^ ^^ ^^ ^^ ^^ ^^^^^
>obj : { ctx: T; a: (a: A, ctx: T) => void; b: (b: B, ctx: T, a: A) => void; }
> : ^^^^^^^ ^^^^^ ^^^^^ ^^^

ctx: T;
>ctx : T
> : ^

a: (a: A, ctx: T) => void;
>a : (a: A, ctx: T) => void
> : ^ ^^ ^^ ^^ ^^^^^
>a : A
> : ^
>ctx : T
> : ^

b: (b: B, ctx: T, a: A) => void;
>b : (b: B, ctx: T, a: A) => void
> : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^
>b : B
> : ^
>ctx : T
> : ^
>a : A
> : ^

}): void;

test({
>test({ ctx: 'foo', a: (a: string, ctx) => {}, b: (b: string, ctx, a) => {},}) : void
> : ^^^^
>test : <T, A, B>(obj: { ctx: T; a: (a: A, ctx: T) => void; b: (b: B, ctx: T, a: A) => void; }) => void
> : ^ ^^ ^^ ^^ ^^ ^^^^^
>{ ctx: 'foo', a: (a: string, ctx) => {}, b: (b: string, ctx, a) => {},} : { ctx: string; a: (a: string, ctx: string) => void; b: (b: string, ctx: string, a: string) => void; }
> : ^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^

ctx: 'foo',
>ctx : string
> : ^^^^^^
>'foo' : "foo"
> : ^^^^^

a: (a: string, ctx) => {},
>a : (a: string, ctx: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^^
>(a: string, ctx) => {} : (a: string, ctx: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^^
>a : string
> : ^^^^^^
>ctx : string
> : ^^^^^^

b: (b: string, ctx, a) => {},
>b : (b: string, ctx: string, a: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
>(b: string, ctx, a) => {} : (b: string, ctx: string, a: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
>b : string
> : ^^^^^^
>ctx : string
> : ^^^^^^
>a : string
> : ^^^^^^

});

test({
>test({ ctx: 'foo', b: (b: string, ctx, a) => {}, a: (a: string, ctx) => {},}) : void
> : ^^^^
>test : <T, A, B>(obj: { ctx: T; a: (a: A, ctx: T) => void; b: (b: B, ctx: T, a: A) => void; }) => void
> : ^ ^^ ^^ ^^ ^^ ^^^^^
>{ ctx: 'foo', b: (b: string, ctx, a) => {}, a: (a: string, ctx) => {},} : { ctx: string; b: (b: string, ctx: string, a: string) => void; a: (a: string, ctx: string) => void; }
> : ^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^

ctx: 'foo',
>ctx : string
> : ^^^^^^
>'foo' : "foo"
> : ^^^^^

b: (b: string, ctx, a) => {},
>b : (b: string, ctx: string, a: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
>(b: string, ctx, a) => {} : (b: string, ctx: string, a: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
>b : string
> : ^^^^^^
>ctx : string
> : ^^^^^^
>a : string
> : ^^^^^^

a: (a: string, ctx) => {},
>a : (a: string, ctx: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^^
>(a: string, ctx) => {} : (a: string, ctx: string) => void
> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^^
>a : string
> : ^^^^^^
>ctx : string
> : ^^^^^^

});

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

=== contextSensitiveAnnotatedParametersInference2.ts ===
// https://github.com/microsoft/TypeScript/issues/60047

type Map<T> = {
>Map : Symbol(Map, Decl(contextSensitiveAnnotatedParametersInference2.ts, 0, 0))
>T : Symbol(T, Decl(contextSensitiveAnnotatedParametersInference2.ts, 2, 9))

[P in keyof T]: T[P] extends boolean ? "boolean" : "other";
>P : Symbol(P, Decl(contextSensitiveAnnotatedParametersInference2.ts, 3, 3))
>T : Symbol(T, Decl(contextSensitiveAnnotatedParametersInference2.ts, 2, 9))
>T : Symbol(T, Decl(contextSensitiveAnnotatedParametersInference2.ts, 2, 9))
>P : Symbol(P, Decl(contextSensitiveAnnotatedParametersInference2.ts, 3, 3))

};

export function buildCommand<F extends Record<string, unknown>>(builderArgs: {
>buildCommand : Symbol(buildCommand, Decl(contextSensitiveAnnotatedParametersInference2.ts, 4, 2))
>F : Symbol(F, Decl(contextSensitiveAnnotatedParametersInference2.ts, 6, 29))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>builderArgs : Symbol(builderArgs, Decl(contextSensitiveAnnotatedParametersInference2.ts, 6, 64))

func: (p: F) => void;
>func : Symbol(func, Decl(contextSensitiveAnnotatedParametersInference2.ts, 6, 78))
>p : Symbol(p, Decl(contextSensitiveAnnotatedParametersInference2.ts, 7, 9))
>F : Symbol(F, Decl(contextSensitiveAnnotatedParametersInference2.ts, 6, 29))

params: Map<NoInfer<F>>;
>params : Symbol(params, Decl(contextSensitiveAnnotatedParametersInference2.ts, 7, 23))
>Map : Symbol(Map, Decl(contextSensitiveAnnotatedParametersInference2.ts, 0, 0))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>F : Symbol(F, Decl(contextSensitiveAnnotatedParametersInference2.ts, 6, 29))

}) {}

type Foo = { foo: boolean };
>Foo : Symbol(Foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 9, 5))
>foo : Symbol(foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 11, 12))

buildCommand({
>buildCommand : Symbol(buildCommand, Decl(contextSensitiveAnnotatedParametersInference2.ts, 4, 2))

func: function (p: Foo) {},
>func : Symbol(func, Decl(contextSensitiveAnnotatedParametersInference2.ts, 13, 14))
>p : Symbol(p, Decl(contextSensitiveAnnotatedParametersInference2.ts, 14, 18))
>Foo : Symbol(Foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 9, 5))

params: {
>params : Symbol(params, Decl(contextSensitiveAnnotatedParametersInference2.ts, 14, 29))

foo: "boolean",
>foo : Symbol(foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 15, 11))

},
});

buildCommand({
>buildCommand : Symbol(buildCommand, Decl(contextSensitiveAnnotatedParametersInference2.ts, 4, 2))

func(p: Foo) {},
>func : Symbol(func, Decl(contextSensitiveAnnotatedParametersInference2.ts, 20, 14))
>p : Symbol(p, Decl(contextSensitiveAnnotatedParametersInference2.ts, 21, 7))
>Foo : Symbol(Foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 9, 5))

params: {
>params : Symbol(params, Decl(contextSensitiveAnnotatedParametersInference2.ts, 21, 18))

foo: "boolean",
>foo : Symbol(foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 22, 11))

},
});

buildCommand({
>buildCommand : Symbol(buildCommand, Decl(contextSensitiveAnnotatedParametersInference2.ts, 4, 2))

func: (p: Foo) => {},
>func : Symbol(func, Decl(contextSensitiveAnnotatedParametersInference2.ts, 27, 14))
>p : Symbol(p, Decl(contextSensitiveAnnotatedParametersInference2.ts, 28, 9))
>Foo : Symbol(Foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 9, 5))

params: {
>params : Symbol(params, Decl(contextSensitiveAnnotatedParametersInference2.ts, 28, 23))

foo: "boolean",
>foo : Symbol(foo, Decl(contextSensitiveAnnotatedParametersInference2.ts, 29, 11))

},
});

Loading