Skip to content

Commit f5b2d9b

Browse files
authored
Unwrap NoInfer types when narrowing (#58292)
1 parent 22eaccb commit f5b2d9b

File tree

5 files changed

+295
-4
lines changed

5 files changed

+295
-4
lines changed

src/compiler/checker.ts

+3
Original file line numberDiff line numberDiff line change
@@ -29479,6 +29479,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2947929479
}
2948029480

2948129481
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
29482+
if (isNoInferType(type)) {
29483+
type = (type as SubstitutionType).baseType;
29484+
}
2948229485
// When the type of a reference is or contains an instantiable type with a union type constraint, and
2948329486
// when the reference is in a constraint position (where it is known we'll obtain the apparent type) or
2948429487
// has a contextual type containing no top-level instantiables (meaning constraints will determine
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//// [tests/cases/compiler/narrowingNoInfer1.ts] ////
2+
3+
=== narrowingNoInfer1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/58266
5+
6+
type TaggedA = { _tag: "a" };
7+
>TaggedA : Symbol(TaggedA, Decl(narrowingNoInfer1.ts, 0, 0))
8+
>_tag : Symbol(_tag, Decl(narrowingNoInfer1.ts, 2, 16))
9+
10+
type TaggedB = { _tag: "b" };
11+
>TaggedB : Symbol(TaggedB, Decl(narrowingNoInfer1.ts, 2, 29))
12+
>_tag : Symbol(_tag, Decl(narrowingNoInfer1.ts, 3, 16))
13+
14+
type TaggedUnion = TaggedA | TaggedB;
15+
>TaggedUnion : Symbol(TaggedUnion, Decl(narrowingNoInfer1.ts, 3, 29))
16+
>TaggedA : Symbol(TaggedA, Decl(narrowingNoInfer1.ts, 0, 0))
17+
>TaggedB : Symbol(TaggedB, Decl(narrowingNoInfer1.ts, 2, 29))
18+
19+
const m: { result: NoInfer<TaggedUnion> }[] = [];
20+
>m : Symbol(m, Decl(narrowingNoInfer1.ts, 7, 5))
21+
>result : Symbol(result, Decl(narrowingNoInfer1.ts, 7, 10))
22+
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
23+
>TaggedUnion : Symbol(TaggedUnion, Decl(narrowingNoInfer1.ts, 3, 29))
24+
25+
function map<A, B>(items: readonly A[], f: (a: NoInfer<A>) => B) {
26+
>map : Symbol(map, Decl(narrowingNoInfer1.ts, 7, 49))
27+
>A : Symbol(A, Decl(narrowingNoInfer1.ts, 9, 13))
28+
>B : Symbol(B, Decl(narrowingNoInfer1.ts, 9, 15))
29+
>items : Symbol(items, Decl(narrowingNoInfer1.ts, 9, 19))
30+
>A : Symbol(A, Decl(narrowingNoInfer1.ts, 9, 13))
31+
>f : Symbol(f, Decl(narrowingNoInfer1.ts, 9, 39))
32+
>a : Symbol(a, Decl(narrowingNoInfer1.ts, 9, 44))
33+
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
34+
>A : Symbol(A, Decl(narrowingNoInfer1.ts, 9, 13))
35+
>B : Symbol(B, Decl(narrowingNoInfer1.ts, 9, 15))
36+
37+
return items.map(f);
38+
>items.map : Symbol(ReadonlyArray.map, Decl(lib.es5.d.ts, --, --))
39+
>items : Symbol(items, Decl(narrowingNoInfer1.ts, 9, 19))
40+
>map : Symbol(ReadonlyArray.map, Decl(lib.es5.d.ts, --, --))
41+
>f : Symbol(f, Decl(narrowingNoInfer1.ts, 9, 39))
42+
}
43+
44+
const something = map(m, (_) =>
45+
>something : Symbol(something, Decl(narrowingNoInfer1.ts, 13, 5))
46+
>map : Symbol(map, Decl(narrowingNoInfer1.ts, 7, 49))
47+
>m : Symbol(m, Decl(narrowingNoInfer1.ts, 7, 5))
48+
>_ : Symbol(_, Decl(narrowingNoInfer1.ts, 13, 26))
49+
50+
_.result._tag === "a" ? { ..._, result: _.result } : null,
51+
>_.result._tag : Symbol(_tag, Decl(narrowingNoInfer1.ts, 2, 16), Decl(narrowingNoInfer1.ts, 3, 16))
52+
>_.result : Symbol(result, Decl(narrowingNoInfer1.ts, 7, 10))
53+
>_ : Symbol(_, Decl(narrowingNoInfer1.ts, 13, 26))
54+
>result : Symbol(result, Decl(narrowingNoInfer1.ts, 7, 10))
55+
>_tag : Symbol(_tag, Decl(narrowingNoInfer1.ts, 2, 16), Decl(narrowingNoInfer1.ts, 3, 16))
56+
>_ : Symbol(_, Decl(narrowingNoInfer1.ts, 13, 26))
57+
>result : Symbol(result, Decl(narrowingNoInfer1.ts, 14, 33))
58+
>_.result : Symbol(result, Decl(narrowingNoInfer1.ts, 7, 10))
59+
>_ : Symbol(_, Decl(narrowingNoInfer1.ts, 13, 26))
60+
>result : Symbol(result, Decl(narrowingNoInfer1.ts, 7, 10))
61+
62+
);
63+
64+
declare function test2<T1, T2>(a: T1, b: T2, cb: (thing: NoInfer<T1> | NoInfer<T2>) => void): void;
65+
>test2 : Symbol(test2, Decl(narrowingNoInfer1.ts, 15, 2))
66+
>T1 : Symbol(T1, Decl(narrowingNoInfer1.ts, 17, 23))
67+
>T2 : Symbol(T2, Decl(narrowingNoInfer1.ts, 17, 26))
68+
>a : Symbol(a, Decl(narrowingNoInfer1.ts, 17, 31))
69+
>T1 : Symbol(T1, Decl(narrowingNoInfer1.ts, 17, 23))
70+
>b : Symbol(b, Decl(narrowingNoInfer1.ts, 17, 37))
71+
>T2 : Symbol(T2, Decl(narrowingNoInfer1.ts, 17, 26))
72+
>cb : Symbol(cb, Decl(narrowingNoInfer1.ts, 17, 44))
73+
>thing : Symbol(thing, Decl(narrowingNoInfer1.ts, 17, 50))
74+
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
75+
>T1 : Symbol(T1, Decl(narrowingNoInfer1.ts, 17, 23))
76+
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
77+
>T2 : Symbol(T2, Decl(narrowingNoInfer1.ts, 17, 26))
78+
79+
test2({ type: 'a' as const }, { type: 'b' as const }, (thing) => {
80+
>test2 : Symbol(test2, Decl(narrowingNoInfer1.ts, 15, 2))
81+
>type : Symbol(type, Decl(narrowingNoInfer1.ts, 19, 7))
82+
>const : Symbol(const)
83+
>type : Symbol(type, Decl(narrowingNoInfer1.ts, 19, 31))
84+
>const : Symbol(const)
85+
>thing : Symbol(thing, Decl(narrowingNoInfer1.ts, 19, 55))
86+
87+
if (thing.type === "a") {
88+
>thing.type : Symbol(type, Decl(narrowingNoInfer1.ts, 19, 7), Decl(narrowingNoInfer1.ts, 19, 31))
89+
>thing : Symbol(thing, Decl(narrowingNoInfer1.ts, 19, 55))
90+
>type : Symbol(type, Decl(narrowingNoInfer1.ts, 19, 7), Decl(narrowingNoInfer1.ts, 19, 31))
91+
92+
thing;
93+
>thing : Symbol(thing, Decl(narrowingNoInfer1.ts, 19, 55))
94+
95+
} else {
96+
thing;
97+
>thing : Symbol(thing, Decl(narrowingNoInfer1.ts, 19, 55))
98+
}
99+
});
100+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//// [tests/cases/compiler/narrowingNoInfer1.ts] ////
2+
3+
=== narrowingNoInfer1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/58266
5+
6+
type TaggedA = { _tag: "a" };
7+
>TaggedA : TaggedA
8+
> : ^^^^^^^
9+
>_tag : "a"
10+
> : ^^^
11+
12+
type TaggedB = { _tag: "b" };
13+
>TaggedB : TaggedB
14+
> : ^^^^^^^
15+
>_tag : "b"
16+
> : ^^^
17+
18+
type TaggedUnion = TaggedA | TaggedB;
19+
>TaggedUnion : TaggedUnion
20+
> : ^^^^^^^^^^^
21+
22+
const m: { result: NoInfer<TaggedUnion> }[] = [];
23+
>m : { result: NoInfer<TaggedUnion>; }[]
24+
> : ^^^^^^^^^^ ^^^^^
25+
>result : NoInfer<TaggedUnion>
26+
> : ^^^^^^^^^^^^^^^^^^^^
27+
>[] : never[]
28+
> : ^^^^^^^
29+
30+
function map<A, B>(items: readonly A[], f: (a: NoInfer<A>) => B) {
31+
>map : <A, B>(items: readonly A[], f: (a: NoInfer<A>) => B) => B[]
32+
> : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^
33+
>items : readonly A[]
34+
> : ^^^^^^^^^^^^
35+
>f : (a: NoInfer<A>) => B
36+
> : ^ ^^ ^^^^^
37+
>a : NoInfer<A>
38+
> : ^^^^^^^^^^
39+
40+
return items.map(f);
41+
>items.map(f) : B[]
42+
> : ^^^
43+
>items.map : <U>(callbackfn: (value: A, index: number, array: readonly A[]) => U, thisArg?: any) => U[]
44+
> : ^^^^ ^^^ ^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^
45+
>items : readonly A[]
46+
> : ^^^^^^^^^^^^
47+
>map : <U>(callbackfn: (value: A, index: number, array: readonly A[]) => U, thisArg?: any) => U[]
48+
> : ^^^^ ^^^ ^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^
49+
>f : (a: NoInfer<A>) => B
50+
> : ^ ^^ ^^^^^
51+
}
52+
53+
const something = map(m, (_) =>
54+
>something : ({ result: TaggedA; } | null)[]
55+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
>map(m, (_) => _.result._tag === "a" ? { ..._, result: _.result } : null,) : ({ result: TaggedA; } | null)[]
57+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
58+
>map : <A, B>(items: readonly A[], f: (a: NoInfer<A>) => B) => B[]
59+
> : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^
60+
>m : { result: NoInfer<TaggedUnion>; }[]
61+
> : ^^^^^^^^^^ ^^^^^
62+
>(_) => _.result._tag === "a" ? { ..._, result: _.result } : null : (_: NoInfer<{ result: NoInfer<TaggedUnion>; }>) => { result: TaggedA; } | null
63+
> : ^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64+
>_ : NoInfer<{ result: NoInfer<TaggedUnion>; }>
65+
> : ^^^^^^^^^^^^^^^^^^ ^^^^
66+
67+
_.result._tag === "a" ? { ..._, result: _.result } : null,
68+
>_.result._tag === "a" ? { ..._, result: _.result } : null : { result: TaggedA; } | null
69+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^
70+
>_.result._tag === "a" : boolean
71+
> : ^^^^^^^
72+
>_.result._tag : "a" | "b"
73+
> : ^^^^^^^^^
74+
>_.result : TaggedUnion
75+
> : ^^^^^^^^^^^
76+
>_ : { result: NoInfer<TaggedUnion>; }
77+
> : ^^^^^^^^^^ ^^^
78+
>result : TaggedUnion
79+
> : ^^^^^^^^^^^
80+
>_tag : "a" | "b"
81+
> : ^^^^^^^^^
82+
>"a" : "a"
83+
> : ^^^
84+
>{ ..._, result: _.result } : { result: TaggedA; }
85+
> : ^^^^^^^^^^^^^^^^^^^^
86+
>_ : { result: NoInfer<TaggedUnion>; }
87+
> : ^^^^^^^^^^ ^^^
88+
>result : TaggedA
89+
> : ^^^^^^^
90+
>_.result : TaggedA
91+
> : ^^^^^^^
92+
>_ : { result: NoInfer<TaggedUnion>; }
93+
> : ^^^^^^^^^^ ^^^
94+
>result : TaggedA
95+
> : ^^^^^^^
96+
97+
);
98+
99+
declare function test2<T1, T2>(a: T1, b: T2, cb: (thing: NoInfer<T1> | NoInfer<T2>) => void): void;
100+
>test2 : <T1, T2>(a: T1, b: T2, cb: (thing: NoInfer<T1> | NoInfer<T2>) => void) => void
101+
> : ^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^^^
102+
>a : T1
103+
> : ^^
104+
>b : T2
105+
> : ^^
106+
>cb : (thing: NoInfer<T1> | NoInfer<T2>) => void
107+
> : ^ ^^ ^^^^^
108+
>thing : NoInfer<T1> | NoInfer<T2>
109+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^
110+
111+
test2({ type: 'a' as const }, { type: 'b' as const }, (thing) => {
112+
>test2({ type: 'a' as const }, { type: 'b' as const }, (thing) => { if (thing.type === "a") { thing; } else { thing; }}) : void
113+
> : ^^^^
114+
>test2 : <T1, T2>(a: T1, b: T2, cb: (thing: NoInfer<T1> | NoInfer<T2>) => void) => void
115+
> : ^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^^^
116+
>{ type: 'a' as const } : { type: "a"; }
117+
> : ^^^^^^^^^^^^^^
118+
>type : "a"
119+
> : ^^^
120+
>'a' as const : "a"
121+
> : ^^^
122+
>'a' : "a"
123+
> : ^^^
124+
>{ type: 'b' as const } : { type: "b"; }
125+
> : ^^^^^^^^^^^^^^
126+
>type : "b"
127+
> : ^^^
128+
>'b' as const : "b"
129+
> : ^^^
130+
>'b' : "b"
131+
> : ^^^
132+
>(thing) => { if (thing.type === "a") { thing; } else { thing; }} : (thing: NoInfer<{ type: "a"; }> | NoInfer<{ type: "b"; }>) => void
133+
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
134+
>thing : NoInfer<{ type: "a"; }> | NoInfer<{ type: "b"; }>
135+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
136+
137+
if (thing.type === "a") {
138+
>thing.type === "a" : boolean
139+
> : ^^^^^^^
140+
>thing.type : "a" | "b"
141+
> : ^^^^^^^^^
142+
>thing : NoInfer<{ type: "a"; }> | NoInfer<{ type: "b"; }>
143+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
144+
>type : "a" | "b"
145+
> : ^^^^^^^^^
146+
>"a" : "a"
147+
> : ^^^
148+
149+
thing;
150+
>thing : NoInfer<{ type: "a"; }>
151+
> : ^^^^^^^^^^^^^^^^^^^^^^^
152+
153+
} else {
154+
thing;
155+
>thing : NoInfer<{ type: "b"; }>
156+
> : ^^^^^^^^^^^^^^^^^^^^^^^
157+
}
158+
});
159+

tests/baselines/reference/noInfer.types

+4-4
Original file line numberDiff line numberDiff line change
@@ -460,12 +460,12 @@ class OkClass<T> {
460460
> : ^
461461

462462
return this._value; // ok
463-
>this._value : NoInfer<T>
464-
> : ^^^^^^^^^^
463+
>this._value : T
464+
> : ^
465465
>this : this
466466
> : ^^^^
467-
>_value : NoInfer<T>
468-
> : ^^^^^^^^^^
467+
>_value : T
468+
> : ^
469469
}
470470
}
471471
class OkClass2<T> {
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/58266
5+
6+
type TaggedA = { _tag: "a" };
7+
type TaggedB = { _tag: "b" };
8+
9+
type TaggedUnion = TaggedA | TaggedB;
10+
11+
const m: { result: NoInfer<TaggedUnion> }[] = [];
12+
13+
function map<A, B>(items: readonly A[], f: (a: NoInfer<A>) => B) {
14+
return items.map(f);
15+
}
16+
17+
const something = map(m, (_) =>
18+
_.result._tag === "a" ? { ..._, result: _.result } : null,
19+
);
20+
21+
declare function test2<T1, T2>(a: T1, b: T2, cb: (thing: NoInfer<T1> | NoInfer<T2>) => void): void;
22+
23+
test2({ type: 'a' as const }, { type: 'b' as const }, (thing) => {
24+
if (thing.type === "a") {
25+
thing;
26+
} else {
27+
thing;
28+
}
29+
});

0 commit comments

Comments
 (0)