Skip to content

Commit 7b2b6a8

Browse files
authored
fix(19577): fix regression with fully inferred types and non-null assertions (#50092)
1 parent 4615e52 commit 7b2b6a8

File tree

5 files changed

+257
-4
lines changed

5 files changed

+257
-4
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27936,6 +27936,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2793627936
const isOuterVariable = flowContainer !== declarationContainer;
2793727937
const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent);
2793827938
const isModuleExports = symbol.flags & SymbolFlags.ModuleExports;
27939+
const typeIsAutomatic = type === autoType || type === autoArrayType;
27940+
const isAutomaticTypeInNonNull = typeIsAutomatic && node.parent.kind === SyntaxKind.NonNullExpression;
2793927941
// When the control flow originates in a function expression or arrow function and we are referencing
2794027942
// a const variable or parameter from an outer function, we extend the origin of the control flow
2794127943
// analysis to include the immediately enclosing function.
@@ -27953,10 +27955,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2795327955
node.parent.kind === SyntaxKind.NonNullExpression ||
2795427956
declaration.kind === SyntaxKind.VariableDeclaration && (declaration as VariableDeclaration).exclamationToken ||
2795527957
declaration.flags & NodeFlags.Ambient;
27956-
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, declaration as VariableLikeDeclaration) : type) :
27957-
type === autoType || type === autoArrayType ? undefinedType :
27958-
getOptionalType(type);
27959-
const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer);
27958+
const initialType = isAutomaticTypeInNonNull ? undefinedType :
27959+
assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, declaration as VariableLikeDeclaration) : type) :
27960+
typeIsAutomatic ? undefinedType : getOptionalType(type);
27961+
const flowType = isAutomaticTypeInNonNull ? getNonNullableType(getFlowTypeOfReference(node, type, initialType, flowContainer)) :
27962+
getFlowTypeOfReference(node, type, initialType, flowContainer);
2796027963
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
2796127964
// from declaration to use, and when the variable's declared type doesn't include undefined but the
2796227965
// control flow based type does include undefined.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//// [nonNullFullInference.ts]
2+
// https://github.com/microsoft/TypeScript/issues/19577
3+
4+
function testNonNullInference(numbers: number[]) {
5+
let last;
6+
7+
for (const n of numbers) {
8+
if (n % 2) {
9+
return n;
10+
}
11+
12+
last = n;
13+
}
14+
15+
last;
16+
last!;
17+
}
18+
19+
function testNonNullInferenceWithArrays(numbers: number[]) {
20+
let result;
21+
const arr = [];
22+
23+
for (const n of numbers) {
24+
if (n % 2) {
25+
return [n];
26+
}
27+
28+
arr.push(n);
29+
result = arr;
30+
}
31+
32+
result;
33+
result!;
34+
}
35+
36+
//// [nonNullFullInference.js]
37+
// https://github.com/microsoft/TypeScript/issues/19577
38+
function testNonNullInference(numbers) {
39+
var last;
40+
for (var _i = 0, numbers_1 = numbers; _i < numbers_1.length; _i++) {
41+
var n = numbers_1[_i];
42+
if (n % 2) {
43+
return n;
44+
}
45+
last = n;
46+
}
47+
last;
48+
last;
49+
}
50+
function testNonNullInferenceWithArrays(numbers) {
51+
var result;
52+
var arr = [];
53+
for (var _i = 0, numbers_2 = numbers; _i < numbers_2.length; _i++) {
54+
var n = numbers_2[_i];
55+
if (n % 2) {
56+
return [n];
57+
}
58+
arr.push(n);
59+
result = arr;
60+
}
61+
result;
62+
result;
63+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
=== tests/cases/compiler/nonNullFullInference.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/19577
3+
4+
function testNonNullInference(numbers: number[]) {
5+
>testNonNullInference : Symbol(testNonNullInference, Decl(nonNullFullInference.ts, 0, 0))
6+
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 2, 30))
7+
8+
let last;
9+
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
10+
11+
for (const n of numbers) {
12+
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
13+
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 2, 30))
14+
15+
if (n % 2) {
16+
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
17+
18+
return n;
19+
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
20+
}
21+
22+
last = n;
23+
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
24+
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
25+
}
26+
27+
last;
28+
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
29+
30+
last!;
31+
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
32+
}
33+
34+
function testNonNullInferenceWithArrays(numbers: number[]) {
35+
>testNonNullInferenceWithArrays : Symbol(testNonNullInferenceWithArrays, Decl(nonNullFullInference.ts, 15, 1))
36+
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 17, 40))
37+
38+
let result;
39+
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
40+
41+
const arr = [];
42+
>arr : Symbol(arr, Decl(nonNullFullInference.ts, 19, 9))
43+
44+
for (const n of numbers) {
45+
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
46+
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 17, 40))
47+
48+
if (n % 2) {
49+
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
50+
51+
return [n];
52+
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
53+
}
54+
55+
arr.push(n);
56+
>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
57+
>arr : Symbol(arr, Decl(nonNullFullInference.ts, 19, 9))
58+
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
59+
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
60+
61+
result = arr;
62+
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
63+
>arr : Symbol(arr, Decl(nonNullFullInference.ts, 19, 9))
64+
}
65+
66+
result;
67+
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
68+
69+
result!;
70+
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
71+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
=== tests/cases/compiler/nonNullFullInference.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/19577
3+
4+
function testNonNullInference(numbers: number[]) {
5+
>testNonNullInference : (numbers: number[]) => number
6+
>numbers : number[]
7+
8+
let last;
9+
>last : any
10+
11+
for (const n of numbers) {
12+
>n : number
13+
>numbers : number[]
14+
15+
if (n % 2) {
16+
>n % 2 : number
17+
>n : number
18+
>2 : 2
19+
20+
return n;
21+
>n : number
22+
}
23+
24+
last = n;
25+
>last = n : number
26+
>last : any
27+
>n : number
28+
}
29+
30+
last;
31+
>last : number
32+
33+
last!;
34+
>last! : number
35+
>last : number
36+
}
37+
38+
function testNonNullInferenceWithArrays(numbers: number[]) {
39+
>testNonNullInferenceWithArrays : (numbers: number[]) => number[]
40+
>numbers : number[]
41+
42+
let result;
43+
>result : any
44+
45+
const arr = [];
46+
>arr : any[]
47+
>[] : undefined[]
48+
49+
for (const n of numbers) {
50+
>n : number
51+
>numbers : number[]
52+
53+
if (n % 2) {
54+
>n % 2 : number
55+
>n : number
56+
>2 : 2
57+
58+
return [n];
59+
>[n] : number[]
60+
>n : number
61+
}
62+
63+
arr.push(n);
64+
>arr.push(n) : number
65+
>arr.push : (...items: any[]) => number
66+
>arr : any[]
67+
>push : (...items: any[]) => number
68+
>n : number
69+
70+
result = arr;
71+
>result = arr : number[]
72+
>result : any
73+
>arr : number[]
74+
}
75+
76+
result;
77+
>result : number[]
78+
79+
result!;
80+
>result! : number[]
81+
>result : number[]
82+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// @noImplicitAny: true
2+
// https://github.com/microsoft/TypeScript/issues/19577
3+
4+
function testNonNullInference(numbers: number[]) {
5+
let last;
6+
7+
for (const n of numbers) {
8+
if (n % 2) {
9+
return n;
10+
}
11+
12+
last = n;
13+
}
14+
15+
last;
16+
last!;
17+
}
18+
19+
function testNonNullInferenceWithArrays(numbers: number[]) {
20+
let result;
21+
const arr = [];
22+
23+
for (const n of numbers) {
24+
if (n % 2) {
25+
return [n];
26+
}
27+
28+
arr.push(n);
29+
result = arr;
30+
}
31+
32+
result;
33+
result!;
34+
}

0 commit comments

Comments
 (0)