Skip to content

Add nested call and new expressions as potential intra expression inference sites #54183

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
61 changes: 35 additions & 26 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const node = getParseTreeNode(nodeIn, isJsxAttributeLike);
return node && getContextualTypeForJsxAttribute(node, /*contextFlags*/ undefined);
},
isContextSensitive,
containsContextSensitive,
getTypeOfPropertyOfContextualType,
getFullyQualifiedName,
getResolvedSignature: (node, candidatesOutArray, argumentCount) => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),
Expand Down Expand Up @@ -6428,7 +6428,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function symbolValueDeclarationIsContextSensitive(symbol: Symbol): boolean {
return symbol && !!symbol.valueDeclaration && isExpression(symbol.valueDeclaration) && !isContextSensitive(symbol.valueDeclaration);
return symbol && !!symbol.valueDeclaration && isExpression(symbol.valueDeclaration) && !containsContextSensitive(symbol.valueDeclaration);
}

function toNodeBuilderFlags(flags = TypeFormatFlags.None): NodeBuilderFlags {
Expand Down Expand Up @@ -20006,47 +20006,56 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return createIndexInfo(info.keyType, instantiateType(info.type, mapper), info.isReadonly, info.declaration);
}

// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike | JsxChild): boolean {
function containsContextRelatedNode(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike | JsxChild, predicate: (node: Node) => boolean): boolean {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.FunctionDeclaration: // Function declarations can have context when annotated with a jsdoc @type
return isContextSensitiveFunctionLikeDeclaration(node as FunctionExpression | ArrowFunction | MethodDeclaration);
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return predicate(node);
case SyntaxKind.ObjectLiteralExpression:
return some((node as ObjectLiteralExpression).properties, isContextSensitive);
return some((node as ObjectLiteralExpression).properties, p => containsContextRelatedNode(p, predicate));
case SyntaxKind.ArrayLiteralExpression:
return some((node as ArrayLiteralExpression).elements, isContextSensitive);
return some((node as ArrayLiteralExpression).elements, e => containsContextRelatedNode(e, predicate));
case SyntaxKind.ConditionalExpression:
return isContextSensitive((node as ConditionalExpression).whenTrue) ||
isContextSensitive((node as ConditionalExpression).whenFalse);
return containsContextRelatedNode((node as ConditionalExpression).whenTrue, predicate) ||
containsContextRelatedNode((node as ConditionalExpression).whenFalse, predicate);
case SyntaxKind.BinaryExpression:
return ((node as BinaryExpression).operatorToken.kind === SyntaxKind.BarBarToken || (node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken) &&
(isContextSensitive((node as BinaryExpression).left) || isContextSensitive((node as BinaryExpression).right));
(containsContextRelatedNode((node as BinaryExpression).left, predicate) || containsContextRelatedNode((node as BinaryExpression).right, predicate));
case SyntaxKind.PropertyAssignment:
return isContextSensitive((node as PropertyAssignment).initializer);
return containsContextRelatedNode((node as PropertyAssignment).initializer, predicate);
case SyntaxKind.ParenthesizedExpression:
return isContextSensitive((node as ParenthesizedExpression).expression);
return containsContextRelatedNode((node as ParenthesizedExpression).expression, predicate);
case SyntaxKind.JsxAttributes:
return some((node as JsxAttributes).properties, isContextSensitive) || isJsxOpeningElement(node.parent) && some(node.parent.parent.children, isContextSensitive);
return some((node as JsxAttributes).properties, p => containsContextRelatedNode(p, predicate)) || isJsxOpeningElement(node.parent) && some(node.parent.parent.children, c => containsContextRelatedNode(c, predicate));
case SyntaxKind.JsxAttribute: {
// If there is no initializer, JSX attribute has a boolean value of true which is not context sensitive.
const { initializer } = node as JsxAttribute;
return !!initializer && isContextSensitive(initializer);
return !!initializer && containsContextRelatedNode(initializer, predicate);
}
case SyntaxKind.JsxExpression: {
// It is possible to that node.expression is undefined (e.g <div x={} />)
const { expression } = node as JsxExpression;
return !!expression && isContextSensitive(expression);
return !!expression && containsContextRelatedNode(expression, predicate);
}
}

return false;
}

// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
function containsContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike | JsxChild): boolean {
Copy link
Member

Choose a reason for hiding this comment

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

I guess I wonder what the technical reasons are around why this is this still needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is the question why this is just not replaced by containsContextSensitiveOrCallOrNewExpression everywhere?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah exactly

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tbh, I don't know ;p it has been almost a year since I opened this PR. I'm not sure if there was any specific reason behind this choice or if I just tried to minimize the surface area of this change. I can run an experiment on this later to learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So I ran this experiment and here is the diff

git diff
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 9b37dedb32..ede8bd4bc2 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -20180,7 +20180,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
     // Returns true if the given expression contains (at any level of nesting) a function or arrow expression
     // that is subject to contextual typing.
     function containsContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike | JsxChild): boolean {
-        return containsContextRelatedNode(node, isContextSensitiveFunctionOrObjectLiteralMethod);
+        return containsContextSensitiveOrCallOrNewExpression(node);
     }
 
     function containsContextSensitiveOrCallOrNewExpression(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike | JsxChild): boolean {
diff --git a/tests/baselines/reference/contextuallyTypeAsyncFunctionReturnType.types b/tests/baselines/reference/contextuallyTypeAsyncFunctionReturnType.types
index bf756a3ad4..d9cfa0ba27 100644
--- a/tests/baselines/reference/contextuallyTypeAsyncFunctionReturnType.types
+++ b/tests/baselines/reference/contextuallyTypeAsyncFunctionReturnType.types
@@ -133,7 +133,7 @@ test("windows-process-tree", async () => {
 >test("windows-process-tree", async () => {  return new Promise((resolve, reject) => {    getProcessTree(123, (tree) => {      if (tree) {        resolve();      } else {        reject(new Error("windows-process-tree"));      }    });  });}) : void
 >test : TestFunction
 >"windows-process-tree" : "windows-process-tree"
->async () => {  return new Promise((resolve, reject) => {    getProcessTree(123, (tree) => {      if (tree) {        resolve();      } else {        reject(new Error("windows-process-tree"));      }    });  });} : () => Promise<void>
+>async () => {  return new Promise((resolve, reject) => {    getProcessTree(123, (tree) => {      if (tree) {        resolve();      } else {        reject(new Error("windows-process-tree"));      }    });  });} : (this: Context) => Promise<void>
 
   return new Promise((resolve, reject) => {
 >new Promise((resolve, reject) => {    getProcessTree(123, (tree) => {      if (tree) {        resolve();      } else {        reject(new Error("windows-process-tree"));      }    });  }) : Promise<void>
diff --git a/tests/baselines/reference/deeplyNestedMappedTypes.types b/tests/baselines/reference/deeplyNestedMappedTypes.types
index 4b4cd3cea9..d5f9434801 100644
--- a/tests/baselines/reference/deeplyNestedMappedTypes.types
+++ b/tests/baselines/reference/deeplyNestedMappedTypes.types
@@ -1,7 +1,7 @@
 //// [tests/cases/compiler/deeplyNestedMappedTypes.ts] ////
 
 === Performance Stats ===
-Assignability cache: 500 / 600 (nearest 100)
+Assignability cache: 600 / 600 (nearest 100)
 Type Count: 1,500 / 1,500 (nearest 100)
 Instantiation count: 15,500 / 15,500 (nearest 500)
 Symbol count: 26,000 / 26,000 (nearest 500)
diff --git a/tests/baselines/reference/promiseTypeStrictNull.types b/tests/baselines/reference/promiseTypeStrictNull.types
index 4b9375dd6f..0afffb9aaa 100644
--- a/tests/baselines/reference/promiseTypeStrictNull.types
+++ b/tests/baselines/reference/promiseTypeStrictNull.types
@@ -1,9 +1,9 @@
 //// [tests/cases/compiler/promiseTypeStrictNull.ts] ////
 
 === Performance Stats ===
-Identity cache: 200 / 200 (nearest 100)
+Identity cache: 300 / 300 (nearest 100)
 Assignability cache: 200 / 200 (nearest 100)
-Type Count: 700 / 700 (nearest 100)
+Type Count: 700 / 800 (nearest 100)
 Instantiation count: 2,000 / 2,000 (nearest 500)
 Symbol count: 27,500 / 27,500 (nearest 500)
 
diff --git a/tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.types b/tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.types
index 65a4677dcd..67b08b8aee 100644
--- a/tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.types
+++ b/tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.types
@@ -20,8 +20,8 @@ const editable = () => ({});
 >{} : {}
 
 const mapStateToProps = createStructuredSelector({
->mapStateToProps : Selector<unknown, { editable: {}; }>
->createStructuredSelector({  editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error}) : Selector<unknown, { editable: {}; }>
+>mapStateToProps : Selector<unknown, unknown>
+>createStructuredSelector({  editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error}) : Selector<unknown, unknown>
 >createStructuredSelector : <S, T>(selectors: { [K in keyof T]: Selector<S, T[K]>; }) => Selector<S, T>
 >{  editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error} : { editable: (state: any, props: any) => {}; }

So nothing spectacular has changed here. However, it shows that couple of caches get bigger after this change. The change in the inferred type in reverseMappedTypeContextualTypeNotCircular is slightly worrying but did some extra experiments with the code like this that doesn't error (this test case is meant to error):

type Selector<S, R> = (state: S) => R;

declare function createStructuredSelector<S, T>(selectors: {
  [K in keyof T]: Selector<S, T[K]>;
}): Selector<S, T>;

const editable = () => "";

// current:
// const mapStateToProps: Selector<unknown, { editable: string; }>
// with the change: 
// const mapStateToProps: Selector<any, { editable: string; }>
const mapStateToProps = createStructuredSelector({
  editable: (state: any) => editable(),
});

declare function createStructuredSelector2<S, T>(
  state: S,
  selectors: { [K in keyof T]: Selector<S, T[K]> },
): Selector<S, T>;

const editable2 = () => "";

// current:
// const mapStateToProps21: Selector<{ editable: { foo: string; }; }, { editable: string; }>
// with the change:
// const mapStateToProps21: Selector<any, { editable: string; }>
const mapStateToProps21 = createStructuredSelector2(
  { editable: { foo: "" } },
  {
    editable: (state: any) => editable(),
  },
);

// current:
// const mapStateToProps22: Selector<{ editable: { foo: string; }; }, { editable: string; }>
// with the change:
// const mapStateToProps22: Selector<{ editable: { foo: string; }; }, { editable: string; }>
const mapStateToProps22 = createStructuredSelector2(
  { editable: { foo: "" } },
  {
    editable: (state) => editable(),
  },
);

declare function createStructuredSelector3<S, T>(
  state: S,
  selectors: { [K in keyof T]: Selector<S, T[K]> },
): Selector<S, T>;

const editable3 = (s: { editable: { foo: string } }) => s.editable.foo;

// current:
// const mapStateToProps3: Selector<{ editable: { foo: string; }; }, { editable: string; }>
// with the change:
// const mapStateToProps3: Selector<{ editable: { foo: string; }; }, { editable: string; }>
const mapStateToProps3 = createStructuredSelector2(
  { editable: { foo: "" } },
  {
    editable: (state) => editable3(state),
  },
);

Even though some of those seemingly regressed (any inferred in some of them)... I'm not that sure if that would be the correct assessment of what happens here.

At the moment, inference sources for type variables used within reverse-mapped types are often "ignored". See the corresponding issue and some of my attempts to address the situation here. You can also see some extra instances of how it behaves today weirdly and somewhat inconsistently in my recent comment here.

So to me, it seems that this change just somehow makes such functions with calls/news at the return positions viable inference sources for other type variables

return containsContextRelatedNode(node, isContextSensitiveFunctionOrObjectLiteralMethod);
}

function containsContextSensitiveOrCallOrNewExpression(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike | JsxChild): boolean {
return containsContextRelatedNode(node, n => isContextSensitiveFunctionOrObjectLiteralMethod(n) || isCallOrNewExpression(n));
Copy link
Member

Choose a reason for hiding this comment

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

Feels like you should consider the same of tagged template expressions (unless we currently don't handle those as direct arguments either.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

unless we currently don't handle those as direct arguments either.

I might be mistaken but I think the above is true today:

function test<T extends TemplateStringsArray>(a: T) {}
test`foo`
// ^? function test<TemplateStringsArray>(a: TemplateStringsArray): void

}

function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
return hasContextSensitiveParameters(node) || hasContextSensitiveReturnExpression(node);
}
Expand All @@ -20056,9 +20065,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return false;
}
if (node.body.kind !== SyntaxKind.Block) {
return isContextSensitive(node.body);
return containsContextSensitive(node.body);
}
return !!forEachReturnStatement(node.body as Block, statement => !!statement.expression && isContextSensitive(statement.expression));
return !!forEachReturnStatement(node.body as Block, statement => !!statement.expression && containsContextSensitive(statement.expression));
}

function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration {
Expand Down Expand Up @@ -31379,7 +31388,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const type = checkExpressionForMutableLocation(e, checkMode, forceTuple);
elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression));
elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required);
if (inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) {
if (inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && containsContextSensitiveOrCallOrNewExpression(e)) {
const inferenceContext = getInferenceContext(node);
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
addIntraExpressionInferenceSite(inferenceContext, e, type);
Expand Down Expand Up @@ -31614,7 +31623,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

if (
contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)
(memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && containsContextSensitiveOrCallOrNewExpression(memberDecl)
) {
const inferenceContext = getInferenceContext(node);
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
Expand Down Expand Up @@ -31873,7 +31882,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
addDeprecatedSuggestion(attributeDecl.name, prop.declarations, attributeDecl.name.escapedText as string);
}
}
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(attributeDecl)) {
if (contextualType && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && containsContextSensitiveOrCallOrNewExpression(attributeDecl)) {
const inferenceContext = getInferenceContext(attributes);
Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
const inferenceNode = (attributeDecl.initializer as JsxExpression).expression!;
Expand Down Expand Up @@ -34595,7 +34604,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// For a decorator, no arguments are susceptible to contextual typing due to the fact
// decorators are applied to a declaration by the emitter, and not to an expression.
const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters;
let argCheckMode = !isDecorator && !isSingleNonGenericCandidate && some(args, isContextSensitive) ? CheckMode.SkipContextSensitive : CheckMode.Normal;
let argCheckMode = !isDecorator && !isSingleNonGenericCandidate && some(args, containsContextSensitive) ? CheckMode.SkipContextSensitive : CheckMode.Normal;

// The following variables are captured and modified by calls to chooseOverload.
// If overload resolution or type argument inference fails, we want to report the
Expand Down Expand Up @@ -37409,7 +37418,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

// The identityMapper object is used to indicate that function expressions are wildcards
if (checkMode && checkMode & CheckMode.SkipContextSensitive && isContextSensitive(node)) {
if (checkMode && checkMode & CheckMode.SkipContextSensitive && containsContextSensitive(node)) {
// Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage
if (!getEffectiveReturnTypeNode(node) && !hasContextSensitiveParameters(node)) {
// Return plain anyFunctionType if there is no possibility we'll make inferences from the return type
Expand Down Expand Up @@ -37454,7 +37463,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (!signature) {
return;
}
if (isContextSensitive(node)) {
if (containsContextSensitive(node)) {
if (contextualSignature) {
const inferenceContext = getInferenceContext(node);
let instantiatedContextualSignature: Signature | undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5044,7 +5044,7 @@ export interface TypeChecker {
/** @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined;
/** @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined;
/** @internal */ getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined;
/** @internal */ isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean;
/** @internal */ containsContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean;
/** @internal */ getTypeOfPropertyOfContextualType(type: Type, name: __String): Type | undefined;

/**
Expand Down
2 changes: 1 addition & 1 deletion src/services/refactors/extractSymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1370,7 +1370,7 @@ function extractConstantInScope(
: getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file);
const isJS = isInJSFile(scope);

let variableType = isJS || !checker.isContextSensitive(node)
let variableType = isJS || !checker.containsContextSensitive(node)
? undefined
: checker.typeToTypeNode(checker.getContextualType(node)!, scope, NodeBuilderFlags.NoTruncation); // TODO: GH#18217

Expand Down
23 changes: 23 additions & 0 deletions tests/baselines/reference/intraExpressionInferences.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,27 @@ intraExpressionInferences.ts(133,26): error TS2339: Property 'nonexistent' does
},
consumer: (val) => {},
});

type ErrorFn = (error: unknown) => void;

declare const genericFn: <T>(args: {
parser: (p: unknown, errorFn: ErrorFn) => T;
handler: (data: { body: T }) => unknown;
}) => T;

declare const createParser: <T>(arg: T) => (p: unknown, errorFn: ErrorFn) => T;

genericFn({
parser: createParser(1 as const),
handler: ({ body: _ }) => {},
});

declare const genericFnTuple: <T>(
args: [
parser: (p: unknown, errorFn: ErrorFn) => T,
handler: (data: { body: T }) => unknown
]
) => T;

genericFnTuple([createParser(1 as const), ({ body: _ }) => {}]);

46 changes: 46 additions & 0 deletions tests/baselines/reference/intraExpressionInferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,29 @@ const distantRes = distant({
},
consumer: (val) => {},
});

type ErrorFn = (error: unknown) => void;

declare const genericFn: <T>(args: {
parser: (p: unknown, errorFn: ErrorFn) => T;
handler: (data: { body: T }) => unknown;
}) => T;

declare const createParser: <T>(arg: T) => (p: unknown, errorFn: ErrorFn) => T;

genericFn({
parser: createParser(1 as const),
handler: ({ body: _ }) => {},
});

declare const genericFnTuple: <T>(
args: [
parser: (p: unknown, errorFn: ErrorFn) => T,
handler: (data: { body: T }) => unknown
]
) => T;

genericFnTuple([createParser(1 as const), ({ body: _ }) => {}]);


//// [intraExpressionInferences.js]
Expand Down Expand Up @@ -518,6 +541,15 @@ var distantRes = distant({
},
consumer: function (val) { },
});
genericFn({
parser: createParser(1),
handler: function (_b) {
var _ = _b.body;
},
});
genericFnTuple([createParser(1), function (_b) {
var _ = _b.body;
}]);


//// [intraExpressionInferences.d.ts]
Expand Down Expand Up @@ -648,3 +680,17 @@ declare function distant<T>(args: {
consumer: (val: T) => unknown;
}): T;
declare const distantRes: number;
type ErrorFn = (error: unknown) => void;
declare const genericFn: <T>(args: {
parser: (p: unknown, errorFn: ErrorFn) => T;
handler: (data: {
body: T;
}) => unknown;
}) => T;
declare const createParser: <T>(arg: T) => (p: unknown, errorFn: ErrorFn) => T;
declare const genericFnTuple: <T>(args: [
parser: (p: unknown, errorFn: ErrorFn) => T,
handler: (data: {
body: T;
}) => unknown
]) => T;
79 changes: 79 additions & 0 deletions tests/baselines/reference/intraExpressionInferences.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -1076,3 +1076,82 @@ const distantRes = distant({

});

type ErrorFn = (error: unknown) => void;
>ErrorFn : Symbol(ErrorFn, Decl(intraExpressionInferences.ts, 329, 3))
>error : Symbol(error, Decl(intraExpressionInferences.ts, 331, 16))

declare const genericFn: <T>(args: {
>genericFn : Symbol(genericFn, Decl(intraExpressionInferences.ts, 333, 13))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 333, 26))
>args : Symbol(args, Decl(intraExpressionInferences.ts, 333, 29))

parser: (p: unknown, errorFn: ErrorFn) => T;
>parser : Symbol(parser, Decl(intraExpressionInferences.ts, 333, 36))
>p : Symbol(p, Decl(intraExpressionInferences.ts, 334, 11))
>errorFn : Symbol(errorFn, Decl(intraExpressionInferences.ts, 334, 22))
>ErrorFn : Symbol(ErrorFn, Decl(intraExpressionInferences.ts, 329, 3))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 333, 26))

handler: (data: { body: T }) => unknown;
>handler : Symbol(handler, Decl(intraExpressionInferences.ts, 334, 46))
>data : Symbol(data, Decl(intraExpressionInferences.ts, 335, 12))
>body : Symbol(body, Decl(intraExpressionInferences.ts, 335, 19))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 333, 26))

}) => T;
>T : Symbol(T, Decl(intraExpressionInferences.ts, 333, 26))

declare const createParser: <T>(arg: T) => (p: unknown, errorFn: ErrorFn) => T;
>createParser : Symbol(createParser, Decl(intraExpressionInferences.ts, 338, 13))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 338, 29))
>arg : Symbol(arg, Decl(intraExpressionInferences.ts, 338, 32))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 338, 29))
>p : Symbol(p, Decl(intraExpressionInferences.ts, 338, 44))
>errorFn : Symbol(errorFn, Decl(intraExpressionInferences.ts, 338, 55))
>ErrorFn : Symbol(ErrorFn, Decl(intraExpressionInferences.ts, 329, 3))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 338, 29))

genericFn({
>genericFn : Symbol(genericFn, Decl(intraExpressionInferences.ts, 333, 13))

parser: createParser(1 as const),
>parser : Symbol(parser, Decl(intraExpressionInferences.ts, 340, 11))
>createParser : Symbol(createParser, Decl(intraExpressionInferences.ts, 338, 13))
>const : Symbol(const)

handler: ({ body: _ }) => {},
>handler : Symbol(handler, Decl(intraExpressionInferences.ts, 341, 35))
>body : Symbol(body, Decl(intraExpressionInferences.ts, 335, 19))
>_ : Symbol(_, Decl(intraExpressionInferences.ts, 342, 13))

});

declare const genericFnTuple: <T>(
>genericFnTuple : Symbol(genericFnTuple, Decl(intraExpressionInferences.ts, 345, 13))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 345, 31))

args: [
>args : Symbol(args, Decl(intraExpressionInferences.ts, 345, 34))

parser: (p: unknown, errorFn: ErrorFn) => T,
>p : Symbol(p, Decl(intraExpressionInferences.ts, 347, 13))
>errorFn : Symbol(errorFn, Decl(intraExpressionInferences.ts, 347, 24))
>ErrorFn : Symbol(ErrorFn, Decl(intraExpressionInferences.ts, 329, 3))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 345, 31))

handler: (data: { body: T }) => unknown
>data : Symbol(data, Decl(intraExpressionInferences.ts, 348, 14))
>body : Symbol(body, Decl(intraExpressionInferences.ts, 348, 21))
>T : Symbol(T, Decl(intraExpressionInferences.ts, 345, 31))

]
) => T;
>T : Symbol(T, Decl(intraExpressionInferences.ts, 345, 31))

genericFnTuple([createParser(1 as const), ({ body: _ }) => {}]);
>genericFnTuple : Symbol(genericFnTuple, Decl(intraExpressionInferences.ts, 345, 13))
>createParser : Symbol(createParser, Decl(intraExpressionInferences.ts, 338, 13))
>const : Symbol(const)
>body : Symbol(body, Decl(intraExpressionInferences.ts, 348, 21))
>_ : Symbol(_, Decl(intraExpressionInferences.ts, 352, 44))

Loading