fix(19577): fix regression with fully inferred types and non-null assertions (#50092)

This commit is contained in:
Nicolas Rodriguez 2023-04-10 20:32:56 -04:00 committed by GitHub
parent 4615e52d93
commit 7b2b6a8cc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 257 additions and 4 deletions

View File

@ -27936,6 +27936,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const isOuterVariable = flowContainer !== declarationContainer;
const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent);
const isModuleExports = symbol.flags & SymbolFlags.ModuleExports;
const typeIsAutomatic = type === autoType || type === autoArrayType;
const isAutomaticTypeInNonNull = typeIsAutomatic && node.parent.kind === SyntaxKind.NonNullExpression;
// When the control flow originates in a function expression or arrow function and we are referencing
// a const variable or parameter from an outer function, we extend the origin of the control flow
// analysis to include the immediately enclosing function.
@ -27953,10 +27955,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
node.parent.kind === SyntaxKind.NonNullExpression ||
declaration.kind === SyntaxKind.VariableDeclaration && (declaration as VariableDeclaration).exclamationToken ||
declaration.flags & NodeFlags.Ambient;
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, declaration as VariableLikeDeclaration) : type) :
type === autoType || type === autoArrayType ? undefinedType :
getOptionalType(type);
const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer);
const initialType = isAutomaticTypeInNonNull ? undefinedType :
assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, declaration as VariableLikeDeclaration) : type) :
typeIsAutomatic ? undefinedType : getOptionalType(type);
const flowType = isAutomaticTypeInNonNull ? getNonNullableType(getFlowTypeOfReference(node, type, initialType, flowContainer)) :
getFlowTypeOfReference(node, type, initialType, flowContainer);
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
// from declaration to use, and when the variable's declared type doesn't include undefined but the
// control flow based type does include undefined.

View File

@ -0,0 +1,63 @@
//// [nonNullFullInference.ts]
// https://github.com/microsoft/TypeScript/issues/19577
function testNonNullInference(numbers: number[]) {
let last;
for (const n of numbers) {
if (n % 2) {
return n;
}
last = n;
}
last;
last!;
}
function testNonNullInferenceWithArrays(numbers: number[]) {
let result;
const arr = [];
for (const n of numbers) {
if (n % 2) {
return [n];
}
arr.push(n);
result = arr;
}
result;
result!;
}
//// [nonNullFullInference.js]
// https://github.com/microsoft/TypeScript/issues/19577
function testNonNullInference(numbers) {
var last;
for (var _i = 0, numbers_1 = numbers; _i < numbers_1.length; _i++) {
var n = numbers_1[_i];
if (n % 2) {
return n;
}
last = n;
}
last;
last;
}
function testNonNullInferenceWithArrays(numbers) {
var result;
var arr = [];
for (var _i = 0, numbers_2 = numbers; _i < numbers_2.length; _i++) {
var n = numbers_2[_i];
if (n % 2) {
return [n];
}
arr.push(n);
result = arr;
}
result;
result;
}

View File

@ -0,0 +1,71 @@
=== tests/cases/compiler/nonNullFullInference.ts ===
// https://github.com/microsoft/TypeScript/issues/19577
function testNonNullInference(numbers: number[]) {
>testNonNullInference : Symbol(testNonNullInference, Decl(nonNullFullInference.ts, 0, 0))
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 2, 30))
let last;
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
for (const n of numbers) {
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 2, 30))
if (n % 2) {
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
return n;
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
}
last = n;
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
>n : Symbol(n, Decl(nonNullFullInference.ts, 5, 14))
}
last;
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
last!;
>last : Symbol(last, Decl(nonNullFullInference.ts, 3, 7))
}
function testNonNullInferenceWithArrays(numbers: number[]) {
>testNonNullInferenceWithArrays : Symbol(testNonNullInferenceWithArrays, Decl(nonNullFullInference.ts, 15, 1))
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 17, 40))
let result;
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
const arr = [];
>arr : Symbol(arr, Decl(nonNullFullInference.ts, 19, 9))
for (const n of numbers) {
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
>numbers : Symbol(numbers, Decl(nonNullFullInference.ts, 17, 40))
if (n % 2) {
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
return [n];
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
}
arr.push(n);
>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(nonNullFullInference.ts, 19, 9))
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>n : Symbol(n, Decl(nonNullFullInference.ts, 21, 14))
result = arr;
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
>arr : Symbol(arr, Decl(nonNullFullInference.ts, 19, 9))
}
result;
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
result!;
>result : Symbol(result, Decl(nonNullFullInference.ts, 18, 7))
}

View File

@ -0,0 +1,82 @@
=== tests/cases/compiler/nonNullFullInference.ts ===
// https://github.com/microsoft/TypeScript/issues/19577
function testNonNullInference(numbers: number[]) {
>testNonNullInference : (numbers: number[]) => number
>numbers : number[]
let last;
>last : any
for (const n of numbers) {
>n : number
>numbers : number[]
if (n % 2) {
>n % 2 : number
>n : number
>2 : 2
return n;
>n : number
}
last = n;
>last = n : number
>last : any
>n : number
}
last;
>last : number
last!;
>last! : number
>last : number
}
function testNonNullInferenceWithArrays(numbers: number[]) {
>testNonNullInferenceWithArrays : (numbers: number[]) => number[]
>numbers : number[]
let result;
>result : any
const arr = [];
>arr : any[]
>[] : undefined[]
for (const n of numbers) {
>n : number
>numbers : number[]
if (n % 2) {
>n % 2 : number
>n : number
>2 : 2
return [n];
>[n] : number[]
>n : number
}
arr.push(n);
>arr.push(n) : number
>arr.push : (...items: any[]) => number
>arr : any[]
>push : (...items: any[]) => number
>n : number
result = arr;
>result = arr : number[]
>result : any
>arr : number[]
}
result;
>result : number[]
result!;
>result! : number[]
>result : number[]
}

View File

@ -0,0 +1,34 @@
// @noImplicitAny: true
// https://github.com/microsoft/TypeScript/issues/19577
function testNonNullInference(numbers: number[]) {
let last;
for (const n of numbers) {
if (n % 2) {
return n;
}
last = n;
}
last;
last!;
}
function testNonNullInferenceWithArrays(numbers: number[]) {
let result;
const arr = [];
for (const n of numbers) {
if (n % 2) {
return [n];
}
arr.push(n);
result = arr;
}
result;
result!;
}