Skip to content

Commit f6af618

Browse files
authored
Still generate signatures in SkipContextSensitive mode just to match on return types (#25937)
* Still generate signatures in SkipContextSensitive mode just to match on return types * Add cache for context-free type of a signature node * Accept post-merge baseline
1 parent 01f6093 commit f6af618

6 files changed

+130
-0
lines changed

src/compiler/checker.ts

+13
Original file line numberDiff line numberDiff line change
@@ -10369,7 +10369,10 @@ namespace ts {
1036910369
return true;
1037010370
}
1037110371
}
10372+
return hasContextSensitiveReturnExpression(node);
10373+
}
1037210374

10375+
function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) {
1037310376
// TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value.
1037410377
const body = node.body!;
1037510378
return body.kind === SyntaxKind.Block ? false : isContextSensitive(body);
@@ -20704,6 +20707,16 @@ namespace ts {
2070420707

2070520708
// The identityMapper object is used to indicate that function expressions are wildcards
2070620709
if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) {
20710+
// Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage
20711+
if (!getEffectiveReturnTypeNode(node) && hasContextSensitiveReturnExpression(node)) {
20712+
const links = getNodeLinks(node);
20713+
if (links.contextFreeType) {
20714+
return links.contextFreeType;
20715+
}
20716+
const returnType = getReturnTypeFromBody(node, checkMode);
20717+
const singleReturnSignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
20718+
return links.contextFreeType = createAnonymousType(node.symbol, emptySymbols, [singleReturnSignature], emptyArray, undefined, undefined);
20719+
}
2070720720
return anyFunctionType;
2070820721
}
2070920722

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3667,6 +3667,7 @@ namespace ts {
36673667
superCall?: SuperCall; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
36683668
switchTypes?: Type[]; // Cached array of switch case expression types
36693669
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
3670+
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive
36703671
}
36713672

36723673
export const enum TypeFlags {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//// [badInferenceLowerPriorityThanGoodInference.ts]
2+
interface Foo<A> {
3+
a: A;
4+
b: (x: A) => void;
5+
}
6+
7+
declare function canYouInferThis<A>(fn: () => Foo<A>): A;
8+
9+
const result = canYouInferThis(() => ({
10+
a: { BLAH: 33 },
11+
b: x => { }
12+
}))
13+
14+
result.BLAH;
15+
16+
//// [badInferenceLowerPriorityThanGoodInference.js]
17+
var result = canYouInferThis(function () { return ({
18+
a: { BLAH: 33 },
19+
b: function (x) { }
20+
}); });
21+
result.BLAH;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/compiler/badInferenceLowerPriorityThanGoodInference.ts ===
2+
interface Foo<A> {
3+
>Foo : Symbol(Foo, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 0))
4+
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))
5+
6+
a: A;
7+
>a : Symbol(Foo.a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 18))
8+
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))
9+
10+
b: (x: A) => void;
11+
>b : Symbol(Foo.b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 1, 9))
12+
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 2, 8))
13+
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))
14+
}
15+
16+
declare function canYouInferThis<A>(fn: () => Foo<A>): A;
17+
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 3, 1))
18+
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
19+
>fn : Symbol(fn, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 36))
20+
>Foo : Symbol(Foo, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 0))
21+
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
22+
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
23+
24+
const result = canYouInferThis(() => ({
25+
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 5))
26+
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 3, 1))
27+
28+
a: { BLAH: 33 },
29+
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 39))
30+
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))
31+
32+
b: x => { }
33+
>b : Symbol(b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 20))
34+
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 9, 6))
35+
36+
}))
37+
38+
result.BLAH;
39+
>result.BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))
40+
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 5))
41+
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))
42+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/compiler/badInferenceLowerPriorityThanGoodInference.ts ===
2+
interface Foo<A> {
3+
a: A;
4+
>a : A
5+
6+
b: (x: A) => void;
7+
>b : (x: A) => void
8+
>x : A
9+
}
10+
11+
declare function canYouInferThis<A>(fn: () => Foo<A>): A;
12+
>canYouInferThis : <A>(fn: () => Foo<A>) => A
13+
>fn : () => Foo<A>
14+
15+
const result = canYouInferThis(() => ({
16+
>result : { BLAH: number; }
17+
>canYouInferThis(() => ({ a: { BLAH: 33 }, b: x => { }})) : { BLAH: number; }
18+
>canYouInferThis : <A>(fn: () => Foo<A>) => A
19+
>() => ({ a: { BLAH: 33 }, b: x => { }}) : () => { a: { BLAH: number; }; b: (x: { BLAH: number; }) => void; }
20+
>({ a: { BLAH: 33 }, b: x => { }}) : { a: { BLAH: number; }; b: (x: { BLAH: number; }) => void; }
21+
>{ a: { BLAH: 33 }, b: x => { }} : { a: { BLAH: number; }; b: (x: { BLAH: number; }) => void; }
22+
23+
a: { BLAH: 33 },
24+
>a : { BLAH: number; }
25+
>{ BLAH: 33 } : { BLAH: number; }
26+
>BLAH : number
27+
>33 : 33
28+
29+
b: x => { }
30+
>b : (x: { BLAH: number; }) => void
31+
>x => { } : (x: { BLAH: number; }) => void
32+
>x : { BLAH: number; }
33+
34+
}))
35+
36+
result.BLAH;
37+
>result.BLAH : number
38+
>result : { BLAH: number; }
39+
>BLAH : number
40+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
interface Foo<A> {
2+
a: A;
3+
b: (x: A) => void;
4+
}
5+
6+
declare function canYouInferThis<A>(fn: () => Foo<A>): A;
7+
8+
const result = canYouInferThis(() => ({
9+
a: { BLAH: 33 },
10+
b: x => { }
11+
}))
12+
13+
result.BLAH;

0 commit comments

Comments
 (0)