mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Fix definite assignment analysis issue caused by x !== null checks (#49387)
* Check for non-intersected `undefined` type in definite assignment analysis * Add regression test
This commit is contained in:
parent
6c77996337
commit
565249fbbe
@ -87,8 +87,8 @@ namespace ts {
|
||||
NEUndefinedOrNull = 1 << 21, // x != undefined / x != null
|
||||
Truthy = 1 << 22, // x
|
||||
Falsy = 1 << 23, // !x
|
||||
IsUndefined = 1 << 24, // Exactly undefined
|
||||
IsNull = 1 << 25, // Exactly null
|
||||
IsUndefined = 1 << 24, // Contains undefined or intersection with undefined
|
||||
IsNull = 1 << 25, // Contains null or intersection with null
|
||||
IsUndefinedOrNull = IsUndefined | IsNull,
|
||||
All = (1 << 27) - 1,
|
||||
// The following members encode facts about particular kinds of types for use in the getTypeFacts function.
|
||||
@ -18166,6 +18166,10 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function containsUndefinedType(type: Type) {
|
||||
return !!((type.flags & TypeFlags.Union ? (type as UnionType).types[0] : type).flags & TypeFlags.Undefined);
|
||||
}
|
||||
|
||||
function isStringIndexSignatureOnlyType(type: Type): boolean {
|
||||
return type.flags & TypeFlags.Object && !isGenericMappedType(type) && getPropertiesOfType(type).length === 0 && getIndexInfosOfType(type).length === 1 && !!getIndexInfoOfType(type, stringType) ||
|
||||
type.flags & TypeFlags.UnionOrIntersection && every((type as UnionOrIntersectionType).types, isStringIndexSignatureOnlyType) ||
|
||||
@ -25911,7 +25915,7 @@ namespace ts {
|
||||
return convertAutoToAny(flowType);
|
||||
}
|
||||
}
|
||||
else if (!assumeInitialized && !(getTypeFacts(type) & TypeFacts.IsUndefined) && getTypeFacts(flowType) & TypeFacts.IsUndefined) {
|
||||
else if (!assumeInitialized && !containsUndefinedType(type) && containsUndefinedType(flowType)) {
|
||||
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
|
||||
// Return the declared type to reduce follow-on errors
|
||||
return type;
|
||||
@ -29268,7 +29272,7 @@ namespace ts {
|
||||
assumeUninitialized = true;
|
||||
}
|
||||
const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType);
|
||||
if (assumeUninitialized && !(getTypeFacts(propType) & TypeFacts.IsUndefined) && getTypeFacts(flowType) & TypeFacts.IsUndefined) {
|
||||
if (assumeUninitialized && !containsUndefinedType(propType) && containsUndefinedType(flowType)) {
|
||||
error(errorNode, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217
|
||||
// Return the declared type to reduce follow-on errors
|
||||
return propType;
|
||||
@ -40255,7 +40259,7 @@ namespace ts {
|
||||
const propName = (member as PropertyDeclaration).name;
|
||||
if (isIdentifier(propName) || isPrivateIdentifier(propName) || isComputedPropertyName(propName)) {
|
||||
const type = getTypeOfSymbol(getSymbolOfNode(member));
|
||||
if (!(type.flags & TypeFlags.AnyOrUnknown || getTypeFacts(type) & TypeFacts.IsUndefined)) {
|
||||
if (!(type.flags & TypeFlags.AnyOrUnknown || containsUndefinedType(type))) {
|
||||
if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) {
|
||||
error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName));
|
||||
}
|
||||
@ -40281,7 +40285,7 @@ namespace ts {
|
||||
setParent(reference, staticBlock);
|
||||
reference.flowNode = staticBlock.returnFlowNode;
|
||||
const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType));
|
||||
if (!(getTypeFacts(flowType) & TypeFacts.IsUndefined)) {
|
||||
if (!containsUndefinedType(flowType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -40297,7 +40301,7 @@ namespace ts {
|
||||
setParent(reference, constructor);
|
||||
reference.flowNode = constructor.returnFlowNode;
|
||||
const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType));
|
||||
return !(getTypeFacts(flowType) & TypeFacts.IsUndefined);
|
||||
return !containsUndefinedType(flowType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -263,4 +263,13 @@ tests/cases/conformance/types/unknown/unknownControlFlow.ts(18,9): error TS2322:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Repro from #49386
|
||||
|
||||
function foo<T>(x: T | null) {
|
||||
let y = x;
|
||||
if (y !== null) {
|
||||
y;
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +258,15 @@ function deepEquals<T>(a: T, b: T): boolean {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Repro from #49386
|
||||
|
||||
function foo<T>(x: T | null) {
|
||||
let y = x;
|
||||
if (y !== null) {
|
||||
y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [unknownControlFlow.js]
|
||||
@ -485,6 +494,13 @@ function deepEquals(a, b) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Repro from #49386
|
||||
function foo(x) {
|
||||
var y = x;
|
||||
if (y !== null) {
|
||||
y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [unknownControlFlow.d.ts]
|
||||
@ -524,3 +540,4 @@ declare function f40(a: string | undefined, b: number | null | undefined): void;
|
||||
declare type QQ<T> = NonNullable<NonNullable<NonNullable<T>>>;
|
||||
declare function f41<T>(a: T): void;
|
||||
declare function deepEquals<T>(a: T, b: T): boolean;
|
||||
declare function foo<T>(x: T | null): void;
|
||||
|
||||
@ -616,3 +616,23 @@ function deepEquals<T>(a: T, b: T): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Repro from #49386
|
||||
|
||||
function foo<T>(x: T | null) {
|
||||
>foo : Symbol(foo, Decl(unknownControlFlow.ts, 258, 1))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 262, 13))
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 262, 16))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 262, 13))
|
||||
|
||||
let y = x;
|
||||
>y : Symbol(y, Decl(unknownControlFlow.ts, 263, 7))
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 262, 16))
|
||||
|
||||
if (y !== null) {
|
||||
>y : Symbol(y, Decl(unknownControlFlow.ts, 263, 7))
|
||||
|
||||
y;
|
||||
>y : Symbol(y, Decl(unknownControlFlow.ts, 263, 7))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -697,3 +697,24 @@ function deepEquals<T>(a: T, b: T): boolean {
|
||||
>true : true
|
||||
}
|
||||
|
||||
// Repro from #49386
|
||||
|
||||
function foo<T>(x: T | null) {
|
||||
>foo : <T>(x: T | null) => void
|
||||
>x : T | null
|
||||
>null : null
|
||||
|
||||
let y = x;
|
||||
>y : T | null
|
||||
>x : T | null
|
||||
|
||||
if (y !== null) {
|
||||
>y !== null : boolean
|
||||
>y : T | null
|
||||
>null : null
|
||||
|
||||
y;
|
||||
>y : T & ({} | undefined)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -260,3 +260,12 @@ function deepEquals<T>(a: T, b: T): boolean {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Repro from #49386
|
||||
|
||||
function foo<T>(x: T | null) {
|
||||
let y = x;
|
||||
if (y !== null) {
|
||||
y;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user