Skip to content

Commit bf301e9

Browse files
committed
Treat reference to empty evolving array as an implicit any[]
1 parent a27a68f commit bf301e9

File tree

1 file changed

+22
-24
lines changed

1 file changed

+22
-24
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8524,9 +8524,11 @@ namespace ts {
85248524
}
85258525

85268526
function createFinalArrayType(elementType: Type) {
8527-
return createArrayType(elementType !== neverType ?
8528-
elementType.flags & TypeFlags.Union ? getUnionType((<UnionType>elementType).types, /*subtypeReduction*/ true) : elementType :
8529-
strictNullChecks ? neverType : undefinedWideningType);
8527+
return elementType.flags & TypeFlags.Never ?
8528+
autoArrayType :
8529+
createArrayType(elementType.flags & TypeFlags.Union ?
8530+
getUnionType((<UnionType>elementType).types, /*subtypeReduction*/ true) :
8531+
elementType);
85308532
}
85318533

85328534
// We perform subtype reduction upon obtaining the final array type from an evolving array type.
@@ -8564,26 +8566,22 @@ namespace ts {
85648566
getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction);
85658567
}
85668568

8567-
// Return true if the given node is 'x' in an 'x.push(value)' or 'x.unshift(value)' operation.
8568-
function isPushOrUnshiftCallTarget(node: Node) {
8569-
const parent = getReferenceRoot(node).parent;
8570-
return parent.kind === SyntaxKind.PropertyAccessExpression &&
8571-
parent.parent.kind === SyntaxKind.CallExpression &&
8572-
isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name);
8573-
}
8574-
8575-
// Return true if the given node is 'x' in an 'x[n] = value' operation, where 'n' is an
8576-
// expression of type any, undefined, or a number-like type.
8577-
function isElementAssignmentTarget(node: Node) {
8569+
// Return true if the given node is 'x' in an 'x.length', x.push(value)', 'x.unshift(value)' or
8570+
// 'x[n] = value' operation, where 'n' is an expression of type any, undefined, or a number-like type.
8571+
function isEvolvingArrayOperationTarget(node: Node) {
85788572
const root = getReferenceRoot(node);
85798573
const parent = root.parent;
8580-
return parent.kind === SyntaxKind.ElementAccessExpression &&
8574+
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
8575+
(<PropertyAccessExpression>parent).name.text === "length" ||
8576+
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
8577+
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
85818578
(<ElementAccessExpression>parent).expression === root &&
85828579
parent.parent.kind === SyntaxKind.BinaryExpression &&
85838580
(<BinaryExpression>parent.parent).operatorToken.kind === SyntaxKind.EqualsToken &&
85848581
(<BinaryExpression>parent.parent).left === parent &&
85858582
!isAssignmentTarget(parent.parent) &&
85868583
isTypeAnyOrAllConstituentTypesHaveKind(checkExpression((<ElementAccessExpression>parent).argumentExpression), TypeFlags.NumberLike | TypeFlags.Undefined);
8584+
return isLengthPushOrUnshift || isElementAssignment;
85878585
}
85888586

85898587
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) {
@@ -8597,11 +8595,11 @@ namespace ts {
85978595
const visitedFlowStart = visitedFlowCount;
85988596
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
85998597
visitedFlowCount = visitedFlowStart;
8600-
// When the reference is 'x' in an 'x.push(value)' or 'x[n] = value' operation, we give type
8601-
// 'any[]' to 'x' instead of using the type determined by control flow analysis such that new
8602-
// element types are not considered errors.
8603-
const isEvolvingArrayInferenceTarget = isEvolvingArrayType(evolvedType) && (isPushOrUnshiftCallTarget(reference) || isElementAssignmentTarget(reference));
8604-
const resultType = isEvolvingArrayInferenceTarget ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
8598+
// When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation,
8599+
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
8600+
// on empty arrays are possible without implicit any errors and new element types can be inferred without
8601+
// type mismatch errors.
8602+
const resultType = isEvolvingArrayType(evolvedType) && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
86058603
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
86068604
return declaredType;
86078605
}
@@ -9355,12 +9353,12 @@ namespace ts {
93559353
// from declaration to use, and when the variable's declared type doesn't include undefined but the
93569354
// control flow based type does include undefined.
93579355
if (type === autoType || type === autoArrayType) {
9358-
if (flowType === type) {
9356+
if (flowType === autoType || flowType === autoArrayType) {
93599357
if (compilerOptions.noImplicitAny) {
9360-
error(declaration.name, Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol), typeToString(type));
9361-
error(node, Diagnostics.Variable_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(type));
9358+
error(declaration.name, Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol), typeToString(flowType));
9359+
error(node, Diagnostics.Variable_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(flowType));
93629360
}
9363-
return convertAutoToAny(type);
9361+
return convertAutoToAny(flowType);
93649362
}
93659363
}
93669364
else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {

0 commit comments

Comments
 (0)