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:
Anders Hejlsberg
2022-06-06 09:51:13 -07:00
committed by GitHub
parent 6c77996337
commit 565249fbbe
6 changed files with 87 additions and 7 deletions

View File

@@ -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);
}