Skip to content

Commit 8827bed

Browse files
authored
Merge pull request #29647 from Microsoft/noConstraintsDuringInference
Only check constraints in final phase of type inference
2 parents ed277b8 + 51ccfb1 commit 8827bed

File tree

7 files changed

+218
-2
lines changed

7 files changed

+218
-2
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14113,7 +14113,9 @@ namespace ts {
1411314113
function mapper(t: Type): Type {
1411414114
for (let i = 0; i < inferences.length; i++) {
1411514115
if (t === inferences[i].typeParameter) {
14116-
inferences[i].isFixed = true;
14116+
if (!(context.flags & InferenceFlags.NoFixing)) {
14117+
inferences[i].isFixed = true;
14118+
}
1411714119
return getInferredType(context, i);
1411814120
}
1411914121
}
@@ -14839,10 +14841,12 @@ namespace ts {
1483914841

1484014842
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
1484114843
if (constraint) {
14844+
context.flags |= InferenceFlags.NoFixing;
1484214845
const instantiatedConstraint = instantiateType(constraint, context);
1484314846
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
1484414847
inference.inferredType = inferredType = instantiatedConstraint;
1484514848
}
14849+
context.flags &= ~InferenceFlags.NoFixing;
1484614850
}
1484714851
}
1484814852

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4341,6 +4341,7 @@ namespace ts {
43414341
None = 0, // No special inference behaviors
43424342
NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
43434343
AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType)
4344+
NoFixing = 1 << 2, // Disable type parameter fixing
43444345
}
43454346

43464347
/**
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//// [inferenceAndSelfReferentialConstraint.ts]
2+
// @strict
3+
4+
// Repro from #29520
5+
6+
type Test<K extends keyof any> = {
7+
[P in K | "foo"]: P extends "foo" ? true : () => any
8+
}
9+
10+
function test<T extends Test<keyof T>>(arg: T) {
11+
return arg;
12+
}
13+
14+
const res1 = test({
15+
foo: true,
16+
bar() {
17+
}
18+
});
19+
20+
const res2 = test({
21+
foo: true,
22+
bar: function () {
23+
}
24+
});
25+
26+
const res3 = test({
27+
foo: true,
28+
bar: () => {
29+
}
30+
});
31+
32+
33+
//// [inferenceAndSelfReferentialConstraint.js]
34+
// @strict
35+
function test(arg) {
36+
return arg;
37+
}
38+
var res1 = test({
39+
foo: true,
40+
bar: function () {
41+
}
42+
});
43+
var res2 = test({
44+
foo: true,
45+
bar: function () {
46+
}
47+
});
48+
var res3 = test({
49+
foo: true,
50+
bar: function () {
51+
}
52+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
=== tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts ===
2+
// @strict
3+
4+
// Repro from #29520
5+
6+
type Test<K extends keyof any> = {
7+
>Test : Symbol(Test, Decl(inferenceAndSelfReferentialConstraint.ts, 0, 0))
8+
>K : Symbol(K, Decl(inferenceAndSelfReferentialConstraint.ts, 4, 10))
9+
10+
[P in K | "foo"]: P extends "foo" ? true : () => any
11+
>P : Symbol(P, Decl(inferenceAndSelfReferentialConstraint.ts, 5, 3))
12+
>K : Symbol(K, Decl(inferenceAndSelfReferentialConstraint.ts, 4, 10))
13+
>P : Symbol(P, Decl(inferenceAndSelfReferentialConstraint.ts, 5, 3))
14+
}
15+
16+
function test<T extends Test<keyof T>>(arg: T) {
17+
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
18+
>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14))
19+
>Test : Symbol(Test, Decl(inferenceAndSelfReferentialConstraint.ts, 0, 0))
20+
>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14))
21+
>arg : Symbol(arg, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 39))
22+
>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14))
23+
24+
return arg;
25+
>arg : Symbol(arg, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 39))
26+
}
27+
28+
const res1 = test({
29+
>res1 : Symbol(res1, Decl(inferenceAndSelfReferentialConstraint.ts, 12, 5))
30+
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
31+
32+
foo: true,
33+
>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 12, 19))
34+
35+
bar() {
36+
>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 13, 12))
37+
}
38+
});
39+
40+
const res2 = test({
41+
>res2 : Symbol(res2, Decl(inferenceAndSelfReferentialConstraint.ts, 18, 5))
42+
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
43+
44+
foo: true,
45+
>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 18, 19))
46+
47+
bar: function () {
48+
>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 19, 12))
49+
}
50+
});
51+
52+
const res3 = test({
53+
>res3 : Symbol(res3, Decl(inferenceAndSelfReferentialConstraint.ts, 24, 5))
54+
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
55+
56+
foo: true,
57+
>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 24, 19))
58+
59+
bar: () => {
60+
>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 25, 12))
61+
}
62+
});
63+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
=== tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts ===
2+
// @strict
3+
4+
// Repro from #29520
5+
6+
type Test<K extends keyof any> = {
7+
>Test : Test<K>
8+
9+
[P in K | "foo"]: P extends "foo" ? true : () => any
10+
>true : true
11+
}
12+
13+
function test<T extends Test<keyof T>>(arg: T) {
14+
>test : <T extends Test<keyof T>>(arg: T) => T
15+
>arg : T
16+
17+
return arg;
18+
>arg : T
19+
}
20+
21+
const res1 = test({
22+
>res1 : { foo: true; bar(): void; }
23+
>test({ foo: true, bar() { }}) : { foo: true; bar(): void; }
24+
>test : <T extends Test<keyof T>>(arg: T) => T
25+
>{ foo: true, bar() { }} : { foo: true; bar(): void; }
26+
27+
foo: true,
28+
>foo : true
29+
>true : true
30+
31+
bar() {
32+
>bar : () => void
33+
}
34+
});
35+
36+
const res2 = test({
37+
>res2 : { foo: true; bar: () => void; }
38+
>test({ foo: true, bar: function () { }}) : { foo: true; bar: () => void; }
39+
>test : <T extends Test<keyof T>>(arg: T) => T
40+
>{ foo: true, bar: function () { }} : { foo: true; bar: () => void; }
41+
42+
foo: true,
43+
>foo : true
44+
>true : true
45+
46+
bar: function () {
47+
>bar : () => void
48+
>function () { } : () => void
49+
}
50+
});
51+
52+
const res3 = test({
53+
>res3 : { foo: true; bar: () => void; }
54+
>test({ foo: true, bar: () => { }}) : { foo: true; bar: () => void; }
55+
>test : <T extends Test<keyof T>>(arg: T) => T
56+
>{ foo: true, bar: () => { }} : { foo: true; bar: () => void; }
57+
58+
foo: true,
59+
>foo : true
60+
>true : true
61+
62+
bar: () => {
63+
>bar : () => void
64+
>() => { } : () => void
65+
}
66+
});
67+

tests/baselines/reference/jsdocTemplateTag3.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function f(t, u, v, w, x) {
5252
}
5353

5454
f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope');
55-
>f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope') : string | number
55+
>f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope') : 101 | "nope"
5656
>f : <T extends { a: number; b: string; }, U, V extends { c: boolean; }, W, X>(t: T, u: U, v: V, w: W, x: X) => W | X
5757
>{ a: 12, b: 'hi', c: null } : { a: number; b: string; c: null; }
5858
>a : number
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @strict
2+
3+
// Repro from #29520
4+
5+
type Test<K extends keyof any> = {
6+
[P in K | "foo"]: P extends "foo" ? true : () => any
7+
}
8+
9+
function test<T extends Test<keyof T>>(arg: T) {
10+
return arg;
11+
}
12+
13+
const res1 = test({
14+
foo: true,
15+
bar() {
16+
}
17+
});
18+
19+
const res2 = test({
20+
foo: true,
21+
bar: function () {
22+
}
23+
});
24+
25+
const res3 = test({
26+
foo: true,
27+
bar: () => {
28+
}
29+
});

0 commit comments

Comments
 (0)