Merge pull request #11432 from Microsoft/controlFlowArrays

Control flow analysis for array construction
This commit is contained in:
Anders Hejlsberg
2016-10-14 16:28:35 -07:00
committed by GitHub
20 changed files with 2233 additions and 101 deletions
+21
View File
@@ -785,6 +785,15 @@ namespace ts {
};
}
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
setFlowNodeReferenced(antecedent);
return <FlowArrayMutation>{
flags: FlowFlags.ArrayMutation,
antecedent,
node
};
}
function finishFlowLabel(flow: FlowLabel): FlowNode {
const antecedents = flow.antecedents;
if (!antecedents) {
@@ -1165,6 +1174,12 @@ namespace ts {
forEachChild(node, bind);
if (operator === SyntaxKind.EqualsToken && !isAssignmentTarget(node)) {
bindAssignmentTargetFlow(node.left);
if (node.left.kind === SyntaxKind.ElementAccessExpression) {
const elementAccess = <ElementAccessExpression>node.left;
if (isNarrowableOperand(elementAccess.expression)) {
currentFlow = createFlowArrayMutation(currentFlow, node);
}
}
}
}
}
@@ -1225,6 +1240,12 @@ namespace ts {
else {
forEachChild(node, bind);
}
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
const propertyAccess = <PropertyAccessExpression>node.expression;
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
currentFlow = createFlowArrayMutation(currentFlow, node);
}
}
}
function getContainerFlags(node: Node): ContainerFlags {
+231 -57
View File
@@ -115,6 +115,7 @@ namespace ts {
const intersectionTypes = createMap<IntersectionType>();
const stringLiteralTypes = createMap<LiteralType>();
const numericLiteralTypes = createMap<LiteralType>();
const evolvingArrayTypes: AnonymousType[] = [];
const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
@@ -176,6 +177,7 @@ namespace ts {
let globalBooleanType: ObjectType;
let globalRegExpType: ObjectType;
let anyArrayType: Type;
let autoArrayType: Type;
let anyReadonlyArrayType: Type;
// The library files are only loaded when the feature is used.
@@ -3061,9 +3063,14 @@ namespace ts {
return undefined;
}
function isAutoVariableInitializer(initializer: Expression) {
const expr = initializer && skipParentheses(initializer);
return !expr || expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>expr) === undefinedSymbol;
function isNullOrUndefined(node: Expression) {
const expr = skipParentheses(node);
return expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>expr) === undefinedSymbol;
}
function isEmptyArrayLiteral(node: Expression) {
const expr = skipParentheses(node);
return expr.kind === SyntaxKind.ArrayLiteralExpression && (<ArrayLiteralExpression>expr).elements.length === 0;
}
function addOptionality(type: Type, optional: boolean): Type {
@@ -3104,12 +3111,18 @@ namespace ts {
return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ declaration.questionToken && includeOptionality);
}
// Use control flow type inference for non-ambient, non-exported var or let variables with no initializer
// or a 'null' or 'undefined' initializer.
if (declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
!(getCombinedNodeFlags(declaration) & NodeFlags.Const) && !(getCombinedModifierFlags(declaration) & ModifierFlags.Export) &&
!isInAmbientContext(declaration) && isAutoVariableInitializer(declaration.initializer)) {
return autoType;
!(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !isInAmbientContext(declaration)) {
// Use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no
// initializer or a 'null' or 'undefined' initializer.
if (!(getCombinedNodeFlags(declaration) & NodeFlags.Const) && (!declaration.initializer || isNullOrUndefined(declaration.initializer))) {
return autoType;
}
// Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array
// literal initializer.
if (declaration.initializer && isEmptyArrayLiteral(declaration.initializer)) {
return autoArrayType;
}
}
if (declaration.kind === SyntaxKind.Parameter) {
@@ -7423,7 +7436,7 @@ namespace ts {
type.flags & TypeFlags.NumberLiteral ? numberType :
type.flags & TypeFlags.BooleanLiteral ? booleanType :
type.flags & TypeFlags.EnumLiteral ? (<EnumLiteralType>type).baseType :
type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(map((<UnionType>type).types, getBaseTypeOfLiteralType)) :
type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(sameMap((<UnionType>type).types, getBaseTypeOfLiteralType)) :
type;
}
@@ -7432,7 +7445,7 @@ namespace ts {
type.flags & TypeFlags.NumberLiteral && type.flags & TypeFlags.FreshLiteral ? numberType :
type.flags & TypeFlags.BooleanLiteral ? booleanType :
type.flags & TypeFlags.EnumLiteral ? (<EnumLiteralType>type).baseType :
type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(map((<UnionType>type).types, getWidenedLiteralType)) :
type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(sameMap((<UnionType>type).types, getWidenedLiteralType)) :
type;
}
@@ -7571,10 +7584,10 @@ namespace ts {
return getWidenedTypeOfObjectLiteral(type);
}
if (type.flags & TypeFlags.Union) {
return getUnionType(map((<UnionType>type).types, getWidenedConstituentType));
return getUnionType(sameMap((<UnionType>type).types, getWidenedConstituentType));
}
if (isArrayType(type) || isTupleType(type)) {
return createTypeReference((<TypeReference>type).target, map((<TypeReference>type).typeArguments, getWidenedType));
return createTypeReference((<TypeReference>type).target, sameMap((<TypeReference>type).typeArguments, getWidenedType));
}
}
return type;
@@ -7992,7 +8005,7 @@ namespace ts {
const widenLiteralTypes = context.inferences[index].topLevel &&
!hasPrimitiveConstraint(signature.typeParameters[index]) &&
(context.inferences[index].isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), signature.typeParameters[index]));
const baseInferences = widenLiteralTypes ? map(inferences, getWidenedLiteralType) : inferences;
const baseInferences = widenLiteralTypes ? sameMap(inferences, getWidenedLiteralType) : inferences;
// Infer widened union or supertype, or the unknown type for no common supertype
const unionOrSuperType = context.inferUnionTypes ? getUnionType(baseInferences, /*subtypeReduction*/ true) : getCommonSupertype(baseInferences);
inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : unknownType;
@@ -8395,6 +8408,13 @@ namespace ts {
getAssignedType(<Expression>node);
}
function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) {
return node.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>node).initializer &&
isEmptyArrayLiteral((<VariableDeclaration>node).initializer) ||
node.kind !== SyntaxKind.BindingElement && node.parent.kind === SyntaxKind.BinaryExpression &&
isEmptyArrayLiteral((<BinaryExpression>node.parent).right);
}
function getReferenceCandidate(node: Expression): Expression {
switch (node.kind) {
case SyntaxKind.ParenthesizedExpression:
@@ -8410,6 +8430,14 @@ namespace ts {
return node;
}
function getReferenceRoot(node: Node): Node {
const parent = node.parent;
return parent.kind === SyntaxKind.ParenthesizedExpression ||
parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.EqualsToken && (<BinaryExpression>parent).left === node ||
parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.CommaToken && (<BinaryExpression>parent).right === node ?
getReferenceRoot(parent) : node;
}
function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) {
if (clause.kind === SyntaxKind.CaseClause) {
const caseType = getRegularTypeOfLiteralType(checkExpression((<CaseClause>clause).expression));
@@ -8495,21 +8523,113 @@ namespace ts {
return incomplete ? { flags: 0, type } : type;
}
// An evolving array type tracks the element types that have so far been seen in an
// 'x.push(value)' or 'x[n] = value' operation along the control flow graph. Evolving
// array types are ultimately converted into manifest array types (using getFinalArrayType)
// and never escape the getFlowTypeOfReference function.
function createEvolvingArrayType(elementType: Type): AnonymousType {
const result = <AnonymousType>createObjectType(TypeFlags.Anonymous);
result.elementType = elementType;
return result;
}
function getEvolvingArrayType(elementType: Type): AnonymousType {
return evolvingArrayTypes[elementType.id] || (evolvingArrayTypes[elementType.id] = createEvolvingArrayType(elementType));
}
// When adding evolving array element types we do not perform subtype reduction. Instead,
// we defer subtype reduction until the evolving array type is finalized into a manifest
// array type.
function addEvolvingArrayElementType(evolvingArrayType: AnonymousType, node: Expression): AnonymousType {
const elementType = getBaseTypeOfLiteralType(checkExpression(node));
return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType]));
}
function isEvolvingArrayType(type: Type) {
return !!(type.flags & TypeFlags.Anonymous && (<AnonymousType>type).elementType);
}
function createFinalArrayType(elementType: Type) {
return elementType.flags & TypeFlags.Never ?
autoArrayType :
createArrayType(elementType.flags & TypeFlags.Union ?
getUnionType((<UnionType>elementType).types, /*subtypeReduction*/ true) :
elementType);
}
// We perform subtype reduction upon obtaining the final array type from an evolving array type.
function getFinalArrayType(evolvingArrayType: AnonymousType): Type {
return evolvingArrayType.finalArrayType || (evolvingArrayType.finalArrayType = createFinalArrayType(evolvingArrayType.elementType));
}
function finalizeEvolvingArrayType(type: Type): Type {
return isEvolvingArrayType(type) ? getFinalArrayType(<AnonymousType>type) : type;
}
function getElementTypeOfEvolvingArrayType(type: Type) {
return isEvolvingArrayType(type) ? (<AnonymousType>type).elementType : neverType;
}
function isEvolvingArrayTypeList(types: Type[]) {
let hasEvolvingArrayType = false;
for (const t of types) {
if (!(t.flags & TypeFlags.Never)) {
if (!isEvolvingArrayType(t)) {
return false;
}
hasEvolvingArrayType = true;
}
}
return hasEvolvingArrayType;
}
// At flow control branch or loop junctions, if the type along every antecedent code path
// is an evolving array type, we construct a combined evolving array type. Otherwise we
// finalize all evolving array types.
function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: boolean) {
return isEvolvingArrayTypeList(types) ?
getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))) :
getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction);
}
// Return true if the given node is 'x' in an 'x.length', x.push(value)', 'x.unshift(value)' or
// 'x[n] = value' operation, where 'n' is an expression of type any, undefined, or a number-like type.
function isEvolvingArrayOperationTarget(node: Node) {
const root = getReferenceRoot(node);
const parent = root.parent;
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
(<PropertyAccessExpression>parent).name.text === "length" ||
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
(<ElementAccessExpression>parent).expression === root &&
parent.parent.kind === SyntaxKind.BinaryExpression &&
(<BinaryExpression>parent.parent).operatorToken.kind === SyntaxKind.EqualsToken &&
(<BinaryExpression>parent.parent).left === parent &&
!isAssignmentTarget(parent.parent) &&
isTypeAnyOrAllConstituentTypesHaveKind(checkExpression((<ElementAccessExpression>parent).argumentExpression), TypeFlags.NumberLike | TypeFlags.Undefined);
return isLengthPushOrUnshift || isElementAssignment;
}
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) {
let key: string;
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
}
const initialType = assumeInitialized ? declaredType :
declaredType === autoType ? undefinedType :
declaredType === autoType || declaredType === autoArrayType ? undefinedType :
includeFalsyTypes(declaredType, TypeFlags.Undefined);
const visitedFlowStart = visitedFlowCount;
const result = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
visitedFlowCount = visitedFlowStart;
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(result, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
// When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation,
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
// on empty arrays are possible without implicit any errors and new element types can be inferred without
// type mismatch errors.
const resultType = isEvolvingArrayType(evolvedType) && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
return declaredType;
}
return result;
return resultType;
function getTypeAtFlowNode(flow: FlowNode): FlowType {
while (true) {
@@ -8546,6 +8666,13 @@ namespace ts {
getTypeAtFlowBranchLabel(<FlowLabel>flow) :
getTypeAtFlowLoopLabel(<FlowLabel>flow);
}
else if (flow.flags & FlowFlags.ArrayMutation) {
type = getTypeAtFlowArrayMutation(<FlowArrayMutation>flow);
if (!type) {
flow = (<FlowArrayMutation>flow).antecedent;
continue;
}
}
else if (flow.flags & FlowFlags.Start) {
// Check if we should continue with the control flow of the containing function.
const container = (<FlowStart>flow).container;
@@ -8558,8 +8685,8 @@ namespace ts {
}
else {
// Unreachable code errors are reported in the binding phase. Here we
// simply return the declared type to reduce follow-on errors.
type = declaredType;
// simply return the non-auto declared type to reduce follow-on errors.
type = convertAutoToAny(declaredType);
}
if (flow.flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
@@ -8580,9 +8707,17 @@ namespace ts {
const flowType = getTypeAtFlowNode(flow.antecedent);
return createFlowType(getBaseTypeOfLiteralType(getTypeFromFlowType(flowType)), isIncomplete(flowType));
}
return declaredType === autoType ? getBaseTypeOfLiteralType(getInitialOrAssignedType(node)) :
declaredType.flags & TypeFlags.Union ? getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node)) :
declaredType;
if (declaredType === autoType || declaredType === autoArrayType) {
if (isEmptyArrayAssignment(node)) {
return getEvolvingArrayType(neverType);
}
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node));
return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType;
}
if (declaredType.flags & TypeFlags.Union) {
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node));
}
return declaredType;
}
// We didn't have a direct match. However, if the reference is a dotted name, this
// may be an assignment to a left hand part of the reference. For example, for a
@@ -8595,24 +8730,56 @@ namespace ts {
return undefined;
}
function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType {
const node = flow.node;
const expr = node.kind === SyntaxKind.CallExpression ?
(<PropertyAccessExpression>(<CallExpression>node).expression).expression :
(<ElementAccessExpression>(<BinaryExpression>node).left).expression;
if (isMatchingReference(reference, getReferenceCandidate(expr))) {
const flowType = getTypeAtFlowNode(flow.antecedent);
const type = getTypeFromFlowType(flowType);
if (isEvolvingArrayType(type)) {
let evolvedType = <AnonymousType>type;
if (node.kind === SyntaxKind.CallExpression) {
for (const arg of (<CallExpression>node).arguments) {
evolvedType = addEvolvingArrayElementType(evolvedType, arg);
}
}
else {
const indexType = checkExpression((<ElementAccessExpression>(<BinaryExpression>node).left).argumentExpression);
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | TypeFlags.Undefined)) {
evolvedType = addEvolvingArrayElementType(evolvedType, (<BinaryExpression>node).right);
}
}
return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType));
}
return flowType;
}
return undefined;
}
function getTypeAtFlowCondition(flow: FlowCondition): FlowType {
const flowType = getTypeAtFlowNode(flow.antecedent);
let type = getTypeFromFlowType(flowType);
if (!(type.flags & TypeFlags.Never)) {
// If we have an antecedent type (meaning we're reachable in some way), we first
// attempt to narrow the antecedent type. If that produces the never type, and if
// the antecedent type is incomplete (i.e. a transient type in a loop), then we
// take the type guard as an indication that control *could* reach here once we
// have the complete type. We proceed by switching to the silent never type which
// doesn't report errors when operators are applied to it. Note that this is the
// *only* place a silent never type is ever generated.
const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0;
type = narrowType(type, flow.expression, assumeTrue);
if (type.flags & TypeFlags.Never && isIncomplete(flowType)) {
type = silentNeverType;
}
const type = getTypeFromFlowType(flowType);
if (type.flags & TypeFlags.Never) {
return flowType;
}
return createFlowType(type, isIncomplete(flowType));
// If we have an antecedent type (meaning we're reachable in some way), we first
// attempt to narrow the antecedent type. If that produces the never type, and if
// the antecedent type is incomplete (i.e. a transient type in a loop), then we
// take the type guard as an indication that control *could* reach here once we
// have the complete type. We proceed by switching to the silent never type which
// doesn't report errors when operators are applied to it. Note that this is the
// *only* place a silent never type is ever generated.
const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0;
const nonEvolvingType = finalizeEvolvingArrayType(type);
const narrowedType = narrowType(nonEvolvingType, flow.expression, assumeTrue);
if (narrowedType === nonEvolvingType) {
return flowType;
}
const incomplete = isIncomplete(flowType);
const resultType = incomplete && narrowedType.flags & TypeFlags.Never ? silentNeverType : narrowedType;
return createFlowType(resultType, incomplete);
}
function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType {
@@ -8655,7 +8822,7 @@ namespace ts {
seenIncomplete = true;
}
}
return createFlowType(getUnionType(antecedentTypes, subtypeReduction), seenIncomplete);
return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction), seenIncomplete);
}
function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType {
@@ -8671,11 +8838,15 @@ namespace ts {
}
// If this flow loop junction and reference are already being processed, return
// the union of the types computed for each branch so far, marked as incomplete.
// We should never see an empty array here because the first antecedent of a loop
// junction is always the non-looping control flow path that leads to the top.
// It is possible to see an empty array in cases where loops are nested and the
// back edge of the outer loop reaches an inner loop that is already being analyzed.
// In such cases we restart the analysis of the inner loop, which will then see
// a non-empty in-process array for the outer loop and eventually terminate because
// the first antecedent of a loop junction is always the non-looping control flow
// path that leads to the top.
for (let i = flowLoopStart; i < flowLoopCount; i++) {
if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) {
return createFlowType(getUnionType(flowLoopTypes[i]), /*incomplete*/ true);
if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key && flowLoopTypes[i].length) {
return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], /*subtypeReduction*/ false), /*incomplete*/ true);
}
}
// Add the flow loop junction and reference to the in-process stack and analyze
@@ -8718,7 +8889,7 @@ namespace ts {
}
// The result is incomplete if the first antecedent (the non-looping control flow path)
// is incomplete.
const result = getUnionType(antecedentTypes, subtypeReduction);
const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction);
if (isIncomplete(firstAntecedentType)) {
return createFlowType(result, /*incomplete*/ true);
}
@@ -9100,6 +9271,10 @@ namespace ts {
}
}
function isConstVariable(symbol: Symbol) {
return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 && getTypeOfSymbol(symbol) !== autoArrayType;
}
function checkIdentifier(node: Identifier): Type {
const symbol = getResolvedSymbol(node);
@@ -9190,30 +9365,28 @@ namespace ts {
// 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.
while (flowContainer !== declarationContainer &&
(flowContainer.kind === SyntaxKind.FunctionExpression ||
flowContainer.kind === SyntaxKind.ArrowFunction ||
isObjectLiteralOrClassExpressionMethod(flowContainer)) &&
(isReadonlySymbol(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) {
while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethod(flowContainer)) &&
(isConstVariable(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) {
flowContainer = getControlFlowContainer(flowContainer);
}
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
// declaration container are the same).
const assumeInitialized = isParameter || isOuterVariable ||
type !== autoType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0) ||
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0) ||
isInAmbientContext(declaration);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, 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.
if (type === autoType) {
if (flowType === autoType) {
if (type === autoType || type === autoArrayType) {
if (flowType === autoType || flowType === autoArrayType) {
if (compilerOptions.noImplicitAny) {
error(declaration.name, Diagnostics.Variable_0_implicitly_has_type_any_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol));
error(node, Diagnostics.Variable_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(anyType));
error(declaration.name, Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol), typeToString(flowType));
error(node, Diagnostics.Variable_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(flowType));
}
return anyType;
return convertAutoToAny(flowType);
}
}
else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
@@ -10657,7 +10830,7 @@ namespace ts {
}
}
return getUnionType(signatures.map(getReturnTypeOfSignature), /*subtypeReduction*/ true);
return getUnionType(map(signatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
}
/// e.g. "props" for React.d.ts,
@@ -10707,7 +10880,7 @@ namespace ts {
}
if (elemType.flags & TypeFlags.Union) {
const types = (<UnionOrIntersectionType>elemType).types;
return getUnionType(types.map(type => {
return getUnionType(map(types, type => {
return getResolvedJsxType(node, type, elemClassType);
}), /*subtypeReduction*/ true);
}
@@ -15966,7 +16139,7 @@ namespace ts {
}
function convertAutoToAny(type: Type) {
return type === autoType ? anyType : type;
return type === autoType ? anyType : type === autoArrayType ? anyArrayType : type;
}
// Check variable, parameter, or property declaration
@@ -19384,6 +19557,7 @@ namespace ts {
}
anyArrayType = createArrayType(anyType);
autoArrayType = createArrayType(autoType);
const symbol = getGlobalSymbol("ReadonlyArray", SymbolFlags.Type, /*diagnostic*/ undefined);
globalReadonlyArrayType = symbol && <GenericType>getTypeOfGlobalSymbol(symbol, /*arity*/ 1);
+22 -2
View File
@@ -268,13 +268,33 @@ namespace ts {
if (array) {
result = [];
for (let i = 0; i < array.length; i++) {
const v = array[i];
result.push(f(v, i));
result.push(f(array[i], i));
}
}
return result;
}
// Maps from T to T and avoids allocation if all elements map to themselves
export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[] {
let result: T[];
if (array) {
for (let i = 0; i < array.length; i++) {
if (result) {
result.push(f(array[i], i));
}
else {
const item = array[i];
const mapped = f(item, i);
if (item !== mapped) {
result = array.slice(0, i);
result.push(mapped);
}
}
}
}
return result || array;
}
/**
* Flattens an array containing a mix of array or non-array elements.
*
+1 -1
View File
@@ -2961,7 +2961,7 @@
"category": "Error",
"code": 7033
},
"Variable '{0}' implicitly has type 'any' in some locations where its type cannot be determined.": {
"Variable '{0}' implicitly has type '{1}' in some locations where its type cannot be determined.": {
"category": "Error",
"code": 7034
},
+12 -2
View File
@@ -1907,8 +1907,9 @@ namespace ts {
TrueCondition = 1 << 5, // Condition known to be true
FalseCondition = 1 << 6, // Condition known to be false
SwitchClause = 1 << 7, // Switch statement clause
Referenced = 1 << 8, // Referenced as antecedent once
Shared = 1 << 9, // Referenced as antecedent more than once
ArrayMutation = 1 << 8, // Potential array mutation
Referenced = 1 << 9, // Referenced as antecedent once
Shared = 1 << 10, // Referenced as antecedent more than once
Label = BranchLabel | LoopLabel,
Condition = TrueCondition | FalseCondition
}
@@ -1951,6 +1952,13 @@ namespace ts {
antecedent: FlowNode;
}
// FlowArrayMutation represents a node potentially mutates an array, i.e. an
// operation of the form 'x.push(value)', 'x.unshift(value)' or 'x[n] = value'.
export interface FlowArrayMutation extends FlowNode {
node: CallExpression | BinaryExpression;
antecedent: FlowNode;
}
export type FlowType = Type | IncompleteType;
// Incomplete types occur during control flow analysis of loops. An IncompleteType
@@ -2748,6 +2756,8 @@ namespace ts {
export interface AnonymousType extends ObjectType {
target?: AnonymousType; // Instantiation target
mapper?: TypeMapper; // Instantiation mapper
elementType?: Type; // Element expressions of evolving array type
finalArrayType?: Type; // Final array type of evolving array type
}
/* @internal */
+4
View File
@@ -1901,6 +1901,10 @@ namespace ts {
return node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "Symbol";
}
export function isPushOrUnshiftIdentifier(node: Identifier) {
return node.text === "push" || node.text === "unshift";
}
export function isModifierKind(token: SyntaxKind): boolean {
switch (token) {
case SyntaxKind.AbstractKeyword:
@@ -0,0 +1,105 @@
tests/cases/compiler/controlFlowArrayErrors.ts(5,9): error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
tests/cases/compiler/controlFlowArrayErrors.ts(6,13): error TS7005: Variable 'x' implicitly has an 'any[]' type.
tests/cases/compiler/controlFlowArrayErrors.ts(12,9): error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
tests/cases/compiler/controlFlowArrayErrors.ts(14,13): error TS7005: Variable 'x' implicitly has an 'any[]' type.
tests/cases/compiler/controlFlowArrayErrors.ts(20,9): error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
tests/cases/compiler/controlFlowArrayErrors.ts(23,9): error TS7005: Variable 'x' implicitly has an 'any[]' type.
tests/cases/compiler/controlFlowArrayErrors.ts(30,12): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string | number'.
tests/cases/compiler/controlFlowArrayErrors.ts(35,12): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string | number'.
tests/cases/compiler/controlFlowArrayErrors.ts(49,5): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((...items: (string | number)[]) => number) | ((...items: boolean[]) => number)' has no compatible call signatures.
tests/cases/compiler/controlFlowArrayErrors.ts(57,12): error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number'.
tests/cases/compiler/controlFlowArrayErrors.ts(61,11): error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
tests/cases/compiler/controlFlowArrayErrors.ts(64,9): error TS7005: Variable 'x' implicitly has an 'any[]' type.
==== tests/cases/compiler/controlFlowArrayErrors.ts (12 errors) ====
declare function cond(): boolean;
function f1() {
let x = []; // Implicit any[] error in some locations
~
!!! error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
let y = x; // Implicit any[] error
~
!!! error TS7005: Variable 'x' implicitly has an 'any[]' type.
x.push(5);
let z = x;
}
function f2() {
let x; // Implicit any[] error in some locations
~
!!! error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
x = [];
let y = x; // Implicit any[] error
~
!!! error TS7005: Variable 'x' implicitly has an 'any[]' type.
x.push(5);
let z = x;
}
function f3() {
let x = []; // Implicit any[] error in some locations
~
!!! error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
x.push(5);
function g() {
x; // Implicit any[] error
~
!!! error TS7005: Variable 'x' implicitly has an 'any[]' type.
}
}
function f4() {
let x;
x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
~~~~
!!! error TS2345: Argument of type 'true' is not assignable to parameter of type 'string | number'.
}
function f5() {
let x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
~~~~
!!! error TS2345: Argument of type 'true' is not assignable to parameter of type 'string | number'.
}
function f6() {
let x;
if (cond()) {
x = [];
x.push(5);
x.push("hello");
}
else {
x = [true]; // Non-evolving array
}
x; // boolean[] | (string | number)[]
x.push(99); // Error
~~~~~~~~~~
!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((...items: (string | number)[]) => number) | ((...items: boolean[]) => number)' has no compatible call signatures.
}
function f7() {
let x = []; // x has evolving array value
x.push(5);
let y = x; // y has non-evolving array value
x.push("hello"); // Ok
y.push("hello"); // Error
~~~~~~~
!!! error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number'.
}
function f8() {
const x = []; // Implicit any[] error in some locations
~
!!! error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined.
x.push(5);
function g() {
x; // Implicit any[] error
~
!!! error TS7005: Variable 'x' implicitly has an 'any[]' type.
}
}
@@ -0,0 +1,125 @@
//// [controlFlowArrayErrors.ts]
declare function cond(): boolean;
function f1() {
let x = []; // Implicit any[] error in some locations
let y = x; // Implicit any[] error
x.push(5);
let z = x;
}
function f2() {
let x; // Implicit any[] error in some locations
x = [];
let y = x; // Implicit any[] error
x.push(5);
let z = x;
}
function f3() {
let x = []; // Implicit any[] error in some locations
x.push(5);
function g() {
x; // Implicit any[] error
}
}
function f4() {
let x;
x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
}
function f5() {
let x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
}
function f6() {
let x;
if (cond()) {
x = [];
x.push(5);
x.push("hello");
}
else {
x = [true]; // Non-evolving array
}
x; // boolean[] | (string | number)[]
x.push(99); // Error
}
function f7() {
let x = []; // x has evolving array value
x.push(5);
let y = x; // y has non-evolving array value
x.push("hello"); // Ok
y.push("hello"); // Error
}
function f8() {
const x = []; // Implicit any[] error in some locations
x.push(5);
function g() {
x; // Implicit any[] error
}
}
//// [controlFlowArrayErrors.js]
function f1() {
var x = []; // Implicit any[] error in some locations
var y = x; // Implicit any[] error
x.push(5);
var z = x;
}
function f2() {
var x; // Implicit any[] error in some locations
x = [];
var y = x; // Implicit any[] error
x.push(5);
var z = x;
}
function f3() {
var x = []; // Implicit any[] error in some locations
x.push(5);
function g() {
x; // Implicit any[] error
}
}
function f4() {
var x;
x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
}
function f5() {
var x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
}
function f6() {
var x;
if (cond()) {
x = [];
x.push(5);
x.push("hello");
}
else {
x = [true]; // Non-evolving array
}
x; // boolean[] | (string | number)[]
x.push(99); // Error
}
function f7() {
var x = []; // x has evolving array value
x.push(5);
var y = x; // y has non-evolving array value
x.push("hello"); // Ok
y.push("hello"); // Error
}
function f8() {
var x = []; // Implicit any[] error in some locations
x.push(5);
function g() {
x; // Implicit any[] error
}
}
@@ -0,0 +1,343 @@
//// [controlFlowArrays.ts]
declare function cond(): boolean;
function f1() {
let x = [];
x[0] = 5;
x[1] = "hello";
x[2] = true;
return x; // (string | number | boolean)[]
}
function f2() {
let x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f3() {
let x;
x = [];
x.push(5, "hello");
return x; // (string | number)[]
}
function f4() {
let x = [];
if (cond()) {
x.push(5);
}
else {
x.push("hello");
}
return x; // (string | number)[]
}
function f5() {
let x;
if (cond()) {
x = [];
x.push(5);
}
else {
x = [];
x.push("hello");
}
return x; // (string | number)[]
}
function f6() {
let x;
if (cond()) {
x = 5;
}
else {
x = [];
x.push("hello");
}
return x; // number | string[]
}
function f7() {
let x = null;
if (cond()) {
x = [];
while (cond()) {
x.push("hello");
}
}
return x; // string[] | null
}
function f8() {
let x = [];
x.push(5);
if (cond()) return x; // number[]
x.push("hello");
if (cond()) return x; // (string | number)[]
x.push(true);
return x; // (string | number | boolean)[]
}
function f9() {
let x = [];
if (cond()) {
x.push(5);
return x; // number[]
}
else {
x.push("hello");
return x; // string[]
}
}
function f10() {
let x = [];
if (cond()) {
x.push(true);
x; // boolean[]
}
else {
x.push(5);
x; // number[]
while (cond()) {
x.push("hello");
}
x; // (string | number)[]
}
x.push(99);
return x; // (string | number | boolean)[]
}
function f11() {
let x = [];
if (x.length === 0) { // x.length ok on implicit any[]
x.push("hello");
}
return x;
}
function f12() {
let x;
x = [];
if (x.length === 0) { // x.length ok on implicit any[]
x.push("hello");
}
return x;
}
function f13() {
var x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f14() {
const x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f15() {
let x = [];
while (cond()) {
while (cond()) {}
x.push("hello");
}
return x; // string[]
}
function f16() {
let x;
let y;
(x = [], x).push(5);
(x.push("hello"), x).push(true);
((x))[3] = { a: 1 };
return x; // (string | number | boolean | { a: number })[]
}
function f17() {
let x = [];
x.unshift(5);
x.unshift("hello");
x.unshift(true);
return x; // (string | number | boolean)[]
}
function f18() {
let x = [];
x.push(5);
x.unshift("hello");
x[2] = true;
return x; // (string | number | boolean)[]
}
//// [controlFlowArrays.js]
function f1() {
var x = [];
x[0] = 5;
x[1] = "hello";
x[2] = true;
return x; // (string | number | boolean)[]
}
function f2() {
var x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f3() {
var x;
x = [];
x.push(5, "hello");
return x; // (string | number)[]
}
function f4() {
var x = [];
if (cond()) {
x.push(5);
}
else {
x.push("hello");
}
return x; // (string | number)[]
}
function f5() {
var x;
if (cond()) {
x = [];
x.push(5);
}
else {
x = [];
x.push("hello");
}
return x; // (string | number)[]
}
function f6() {
var x;
if (cond()) {
x = 5;
}
else {
x = [];
x.push("hello");
}
return x; // number | string[]
}
function f7() {
var x = null;
if (cond()) {
x = [];
while (cond()) {
x.push("hello");
}
}
return x; // string[] | null
}
function f8() {
var x = [];
x.push(5);
if (cond())
return x; // number[]
x.push("hello");
if (cond())
return x; // (string | number)[]
x.push(true);
return x; // (string | number | boolean)[]
}
function f9() {
var x = [];
if (cond()) {
x.push(5);
return x; // number[]
}
else {
x.push("hello");
return x; // string[]
}
}
function f10() {
var x = [];
if (cond()) {
x.push(true);
x; // boolean[]
}
else {
x.push(5);
x; // number[]
while (cond()) {
x.push("hello");
}
x; // (string | number)[]
}
x.push(99);
return x; // (string | number | boolean)[]
}
function f11() {
var x = [];
if (x.length === 0) {
x.push("hello");
}
return x;
}
function f12() {
var x;
x = [];
if (x.length === 0) {
x.push("hello");
}
return x;
}
function f13() {
var x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f14() {
var x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f15() {
var x = [];
while (cond()) {
while (cond()) { }
x.push("hello");
}
return x; // string[]
}
function f16() {
var x;
var y;
(x = [], x).push(5);
(x.push("hello"), x).push(true);
((x))[3] = { a: 1 };
return x; // (string | number | boolean | { a: number })[]
}
function f17() {
var x = [];
x.unshift(5);
x.unshift("hello");
x.unshift(true);
return x; // (string | number | boolean)[]
}
function f18() {
var x = [];
x.push(5);
x.unshift("hello");
x[2] = true;
return x; // (string | number | boolean)[]
}
@@ -0,0 +1,470 @@
=== tests/cases/compiler/controlFlowArrays.ts ===
declare function cond(): boolean;
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
function f1() {
>f1 : Symbol(f1, Decl(controlFlowArrays.ts, 1, 33))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 4, 7))
x[0] = 5;
>x : Symbol(x, Decl(controlFlowArrays.ts, 4, 7))
x[1] = "hello";
>x : Symbol(x, Decl(controlFlowArrays.ts, 4, 7))
x[2] = true;
>x : Symbol(x, Decl(controlFlowArrays.ts, 4, 7))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 4, 7))
}
function f2() {
>f2 : Symbol(f2, Decl(controlFlowArrays.ts, 9, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 12, 7))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 12, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 12, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x.push(true);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 12, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 12, 7))
}
function f3() {
>f3 : Symbol(f3, Decl(controlFlowArrays.ts, 17, 1))
let x;
>x : Symbol(x, Decl(controlFlowArrays.ts, 20, 7))
x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 20, 7))
x.push(5, "hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 20, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // (string | number)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 20, 7))
}
function f4() {
>f4 : Symbol(f4, Decl(controlFlowArrays.ts, 24, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 27, 7))
if (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 27, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
else {
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 27, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
return x; // (string | number)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 27, 7))
}
function f5() {
>f5 : Symbol(f5, Decl(controlFlowArrays.ts, 35, 1))
let x;
>x : Symbol(x, Decl(controlFlowArrays.ts, 38, 7))
if (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 38, 7))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 38, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
else {
x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 38, 7))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 38, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
return x; // (string | number)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 38, 7))
}
function f6() {
>f6 : Symbol(f6, Decl(controlFlowArrays.ts, 48, 1))
let x;
>x : Symbol(x, Decl(controlFlowArrays.ts, 51, 7))
if (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x = 5;
>x : Symbol(x, Decl(controlFlowArrays.ts, 51, 7))
}
else {
x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 51, 7))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 51, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
return x; // number | string[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 51, 7))
}
function f7() {
>f7 : Symbol(f7, Decl(controlFlowArrays.ts, 60, 1))
let x = null;
>x : Symbol(x, Decl(controlFlowArrays.ts, 63, 7))
if (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 63, 7))
while (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 63, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
}
return x; // string[] | null
>x : Symbol(x, Decl(controlFlowArrays.ts, 63, 7))
}
function f8() {
>f8 : Symbol(f8, Decl(controlFlowArrays.ts, 71, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 74, 7))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 74, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
if (cond()) return x; // number[]
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
>x : Symbol(x, Decl(controlFlowArrays.ts, 74, 7))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 74, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
if (cond()) return x; // (string | number)[]
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
>x : Symbol(x, Decl(controlFlowArrays.ts, 74, 7))
x.push(true);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 74, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 74, 7))
}
function f9() {
>f9 : Symbol(f9, Decl(controlFlowArrays.ts, 81, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 84, 7))
if (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 84, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // number[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 84, 7))
}
else {
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 84, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // string[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 84, 7))
}
}
function f10() {
>f10 : Symbol(f10, Decl(controlFlowArrays.ts, 93, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
if (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x.push(true);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x; // boolean[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
}
else {
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x; // number[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
while (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
x; // (string | number)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
}
x.push(99);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 96, 7))
}
function f11() {
>f11 : Symbol(f11, Decl(controlFlowArrays.ts, 111, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 114, 7))
if (x.length === 0) { // x.length ok on implicit any[]
>x.length : Symbol(Array.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 114, 7))
>length : Symbol(Array.length, Decl(lib.d.ts, --, --))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 114, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
return x;
>x : Symbol(x, Decl(controlFlowArrays.ts, 114, 7))
}
function f12() {
>f12 : Symbol(f12, Decl(controlFlowArrays.ts, 119, 1))
let x;
>x : Symbol(x, Decl(controlFlowArrays.ts, 122, 7))
x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 122, 7))
if (x.length === 0) { // x.length ok on implicit any[]
>x.length : Symbol(Array.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 122, 7))
>length : Symbol(Array.length, Decl(lib.d.ts, --, --))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 122, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
return x;
>x : Symbol(x, Decl(controlFlowArrays.ts, 122, 7))
}
function f13() {
>f13 : Symbol(f13, Decl(controlFlowArrays.ts, 128, 1))
var x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 131, 7))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 131, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 131, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x.push(true);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 131, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 131, 7))
}
function f14() {
>f14 : Symbol(f14, Decl(controlFlowArrays.ts, 136, 1))
const x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 139, 9))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 139, 9))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 139, 9))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x.push(true);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 139, 9))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 139, 9))
}
function f15() {
>f15 : Symbol(f15, Decl(controlFlowArrays.ts, 144, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 147, 7))
while (cond()) {
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
while (cond()) {}
>cond : Symbol(cond, Decl(controlFlowArrays.ts, 0, 0))
x.push("hello");
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 147, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
return x; // string[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 147, 7))
}
function f16() {
>f16 : Symbol(f16, Decl(controlFlowArrays.ts, 153, 1))
let x;
>x : Symbol(x, Decl(controlFlowArrays.ts, 156, 7))
let y;
>y : Symbol(y, Decl(controlFlowArrays.ts, 157, 7))
(x = [], x).push(5);
>(x = [], x).push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 156, 7))
>x : Symbol(x, Decl(controlFlowArrays.ts, 156, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
(x.push("hello"), x).push(true);
>(x.push("hello"), x).push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 156, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 156, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
((x))[3] = { a: 1 };
>x : Symbol(x, Decl(controlFlowArrays.ts, 156, 7))
>a : Symbol(a, Decl(controlFlowArrays.ts, 160, 16))
return x; // (string | number | boolean | { a: number })[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 156, 7))
}
function f17() {
>f17 : Symbol(f17, Decl(controlFlowArrays.ts, 162, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 165, 7))
x.unshift(5);
>x.unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 165, 7))
>unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
x.unshift("hello");
>x.unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 165, 7))
>unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
x.unshift(true);
>x.unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 165, 7))
>unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 165, 7))
}
function f18() {
>f18 : Symbol(f18, Decl(controlFlowArrays.ts, 170, 1))
let x = [];
>x : Symbol(x, Decl(controlFlowArrays.ts, 173, 7))
x.push(5);
>x.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 173, 7))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
x.unshift("hello");
>x.unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowArrays.ts, 173, 7))
>unshift : Symbol(Array.unshift, Decl(lib.d.ts, --, --))
x[2] = true;
>x : Symbol(x, Decl(controlFlowArrays.ts, 173, 7))
return x; // (string | number | boolean)[]
>x : Symbol(x, Decl(controlFlowArrays.ts, 173, 7))
}
@@ -0,0 +1,615 @@
=== tests/cases/compiler/controlFlowArrays.ts ===
declare function cond(): boolean;
>cond : () => boolean
function f1() {
>f1 : () => (string | number | boolean)[]
let x = [];
>x : any[]
>[] : never[]
x[0] = 5;
>x[0] = 5 : 5
>x[0] : any
>x : any[]
>0 : 0
>5 : 5
x[1] = "hello";
>x[1] = "hello" : "hello"
>x[1] : any
>x : any[]
>1 : 1
>"hello" : "hello"
x[2] = true;
>x[2] = true : true
>x[2] : any
>x : any[]
>2 : 2
>true : true
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
function f2() {
>f2 : () => (string | number | boolean)[]
let x = [];
>x : any[]
>[] : never[]
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
x.push(true);
>x.push(true) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>true : true
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
function f3() {
>f3 : () => (string | number)[]
let x;
>x : any
x = [];
>x = [] : never[]
>x : any
>[] : never[]
x.push(5, "hello");
>x.push(5, "hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
>"hello" : "hello"
return x; // (string | number)[]
>x : (string | number)[]
}
function f4() {
>f4 : () => (string | number)[]
let x = [];
>x : any[]
>[] : never[]
if (cond()) {
>cond() : boolean
>cond : () => boolean
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
}
else {
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
return x; // (string | number)[]
>x : (string | number)[]
}
function f5() {
>f5 : () => (string | number)[]
let x;
>x : any
if (cond()) {
>cond() : boolean
>cond : () => boolean
x = [];
>x = [] : never[]
>x : any
>[] : never[]
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
}
else {
x = [];
>x = [] : never[]
>x : any
>[] : never[]
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
return x; // (string | number)[]
>x : (string | number)[]
}
function f6() {
>f6 : () => number | string[]
let x;
>x : any
if (cond()) {
>cond() : boolean
>cond : () => boolean
x = 5;
>x = 5 : 5
>x : any
>5 : 5
}
else {
x = [];
>x = [] : never[]
>x : any
>[] : never[]
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
return x; // number | string[]
>x : number | string[]
}
function f7() {
>f7 : () => string[] | null
let x = null;
>x : any
>null : null
if (cond()) {
>cond() : boolean
>cond : () => boolean
x = [];
>x = [] : never[]
>x : any
>[] : never[]
while (cond()) {
>cond() : boolean
>cond : () => boolean
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
}
return x; // string[] | null
>x : string[] | null
}
function f8() {
>f8 : () => (string | number | boolean)[]
let x = [];
>x : any[]
>[] : never[]
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
if (cond()) return x; // number[]
>cond() : boolean
>cond : () => boolean
>x : number[]
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
if (cond()) return x; // (string | number)[]
>cond() : boolean
>cond : () => boolean
>x : (string | number)[]
x.push(true);
>x.push(true) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>true : true
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
function f9() {
>f9 : () => string[] | number[]
let x = [];
>x : any[]
>[] : never[]
if (cond()) {
>cond() : boolean
>cond : () => boolean
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
return x; // number[]
>x : number[]
}
else {
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
return x; // string[]
>x : string[]
}
}
function f10() {
>f10 : () => (string | number | boolean)[]
let x = [];
>x : any[]
>[] : never[]
if (cond()) {
>cond() : boolean
>cond : () => boolean
x.push(true);
>x.push(true) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>true : true
x; // boolean[]
>x : boolean[]
}
else {
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
x; // number[]
>x : number[]
while (cond()) {
>cond() : boolean
>cond : () => boolean
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
x; // (string | number)[]
>x : (string | number)[]
}
x.push(99);
>x.push(99) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>99 : 99
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
function f11() {
>f11 : () => string[]
let x = [];
>x : any[]
>[] : never[]
if (x.length === 0) { // x.length ok on implicit any[]
>x.length === 0 : boolean
>x.length : number
>x : any[]
>length : number
>0 : 0
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
return x;
>x : string[]
}
function f12() {
>f12 : () => string[]
let x;
>x : any
x = [];
>x = [] : never[]
>x : any
>[] : never[]
if (x.length === 0) { // x.length ok on implicit any[]
>x.length === 0 : boolean
>x.length : number
>x : any[]
>length : number
>0 : 0
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
return x;
>x : string[]
}
function f13() {
>f13 : () => (string | number | boolean)[]
var x = [];
>x : any[]
>[] : never[]
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
x.push(true);
>x.push(true) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>true : true
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
function f14() {
>f14 : () => (string | number | boolean)[]
const x = [];
>x : any[]
>[] : never[]
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
x.push(true);
>x.push(true) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>true : true
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
function f15() {
>f15 : () => string[]
let x = [];
>x : any[]
>[] : never[]
while (cond()) {
>cond() : boolean
>cond : () => boolean
while (cond()) {}
>cond() : boolean
>cond : () => boolean
x.push("hello");
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
}
return x; // string[]
>x : string[]
}
function f16() {
>f16 : () => (string | number | boolean | { a: number; })[]
let x;
>x : any
let y;
>y : any
(x = [], x).push(5);
>(x = [], x).push(5) : number
>(x = [], x).push : (...items: any[]) => number
>(x = [], x) : any[]
>x = [], x : any[]
>x = [] : never[]
>x : any
>[] : never[]
>x : any[]
>push : (...items: any[]) => number
>5 : 5
(x.push("hello"), x).push(true);
>(x.push("hello"), x).push(true) : number
>(x.push("hello"), x).push : (...items: any[]) => number
>(x.push("hello"), x) : any[]
>x.push("hello"), x : any[]
>x.push("hello") : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>"hello" : "hello"
>x : any[]
>push : (...items: any[]) => number
>true : true
((x))[3] = { a: 1 };
>((x))[3] = { a: 1 } : { a: number; }
>((x))[3] : any
>((x)) : any[]
>(x) : any[]
>x : any[]
>3 : 3
>{ a: 1 } : { a: number; }
>a : number
>1 : 1
return x; // (string | number | boolean | { a: number })[]
>x : (string | number | boolean | { a: number; })[]
}
function f17() {
>f17 : () => (string | number | boolean)[]
let x = [];
>x : any[]
>[] : never[]
x.unshift(5);
>x.unshift(5) : number
>x.unshift : (...items: any[]) => number
>x : any[]
>unshift : (...items: any[]) => number
>5 : 5
x.unshift("hello");
>x.unshift("hello") : number
>x.unshift : (...items: any[]) => number
>x : any[]
>unshift : (...items: any[]) => number
>"hello" : "hello"
x.unshift(true);
>x.unshift(true) : number
>x.unshift : (...items: any[]) => number
>x : any[]
>unshift : (...items: any[]) => number
>true : true
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
function f18() {
>f18 : () => (string | number | boolean)[]
let x = [];
>x : any[]
>[] : never[]
x.push(5);
>x.push(5) : number
>x.push : (...items: any[]) => number
>x : any[]
>push : (...items: any[]) => number
>5 : 5
x.unshift("hello");
>x.unshift("hello") : number
>x.unshift : (...items: any[]) => number
>x : any[]
>unshift : (...items: any[]) => number
>"hello" : "hello"
x[2] = true;
>x[2] = true : true
>x[2] : any
>x : any[]
>2 : 2
>true : true
return x; // (string | number | boolean)[]
>x : (string | number | boolean)[]
}
@@ -1,17 +1,14 @@
tests/cases/compiler/implicitAnyWidenToAny.ts(4,5): error TS7005: Variable 'widenArray' implicitly has an 'any[]' type.
tests/cases/compiler/implicitAnyWidenToAny.ts(5,5): error TS7005: Variable 'emptyArray' implicitly has an 'any[]' type.
==== tests/cases/compiler/implicitAnyWidenToAny.ts (2 errors) ====
==== tests/cases/compiler/implicitAnyWidenToAny.ts (1 errors) ====
// these should be errors
var x = null; // error at "x"
var x1 = undefined; // error at "x1"
var widenArray = [null, undefined]; // error at "widenArray"
~~~~~~~~~~
!!! error TS7005: Variable 'widenArray' implicitly has an 'any[]' type.
var emptyArray = []; // error at "emptyArray"
~~~~~~~~~~
!!! error TS7005: Variable 'emptyArray' implicitly has an 'any[]' type.
var emptyArray = [];
// these should not be error
class AnimalObj {
@@ -3,7 +3,7 @@
var x = null; // error at "x"
var x1 = undefined; // error at "x1"
var widenArray = [null, undefined]; // error at "widenArray"
var emptyArray = []; // error at "emptyArray"
var emptyArray = [];
// these should not be error
class AnimalObj {
@@ -32,7 +32,7 @@ var obj1 = anyReturnFunc();
var x = null; // error at "x"
var x1 = undefined; // error at "x1"
var widenArray = [null, undefined]; // error at "widenArray"
var emptyArray = []; // error at "emptyArray"
var emptyArray = [];
// these should not be error
var AnimalObj = (function () {
function AnimalObj() {
@@ -35,12 +35,12 @@ use(data[0]() === data[1]());
>use(data[0]() === data[1]()) : any
>use : (a: any) => any
>data[0]() === data[1]() : boolean
>data[0]() : any
>data[0] : any
>data : any[]
>data[0]() : typeof C
>data[0] : () => typeof C
>data : (() => typeof C)[]
>0 : 0
>data[1]() : any
>data[1] : any
>data : any[]
>data[1]() : typeof C
>data[1] : () => typeof C
>data : (() => typeof C)[]
>1 : 1
@@ -36,12 +36,12 @@ use(data[0]() === data[1]());
>use(data[0]() === data[1]()) : any
>use : (a: any) => any
>data[0]() === data[1]() : boolean
>data[0]() : any
>data[0] : any
>data : any[]
>data[0]() : typeof C
>data[0] : () => typeof C
>data : (() => typeof C)[]
>0 : 0
>data[1]() : any
>data[1] : any
>data : any[]
>data[1]() : typeof C
>data[1] : () => typeof C
>data : (() => typeof C)[]
>1 : 1
@@ -14,7 +14,7 @@ var a3 = void 0;
>0 : 0
var b1 = [];
>b1 : never[]
>b1 : any[]
>[] : never[]
var b2 = [,];
+18 -18
View File
@@ -1,7 +1,7 @@
=== tests/cases/compiler/typedArrays.ts ===
function CreateTypedArrayTypes() {
>CreateTypedArrayTypes : () => any[]
>CreateTypedArrayTypes : () => (Int8ArrayConstructor | Uint8ArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor | Uint8ClampedArrayConstructor)[]
var typedArrays = [];
>typedArrays : any[]
@@ -71,11 +71,11 @@ function CreateTypedArrayTypes() {
>Uint8ClampedArray : Uint8ClampedArrayConstructor
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8ArrayConstructor | Uint8ArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor | Uint8ClampedArrayConstructor)[]
}
function CreateTypedArrayInstancesFromLength(obj: number) {
>CreateTypedArrayInstancesFromLength : (obj: number) => any[]
>CreateTypedArrayInstancesFromLength : (obj: number) => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
>obj : number
var typedArrays = [];
@@ -164,11 +164,11 @@ function CreateTypedArrayInstancesFromLength(obj: number) {
>obj : number
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
function CreateTypedArrayInstancesFromArray(obj: number[]) {
>CreateTypedArrayInstancesFromArray : (obj: number[]) => any[]
>CreateTypedArrayInstancesFromArray : (obj: number[]) => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
>obj : number[]
var typedArrays = [];
@@ -257,11 +257,11 @@ function CreateTypedArrayInstancesFromArray(obj: number[]) {
>obj : number[]
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
function CreateIntegerTypedArraysFromArray2(obj:number[]) {
>CreateIntegerTypedArraysFromArray2 : (obj: number[]) => any[]
>CreateIntegerTypedArraysFromArray2 : (obj: number[]) => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
>obj : number[]
var typedArrays = [];
@@ -368,11 +368,11 @@ function CreateIntegerTypedArraysFromArray2(obj:number[]) {
>obj : number[]
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
function CreateIntegerTypedArraysFromArrayLike(obj:ArrayLike<number>) {
>CreateIntegerTypedArraysFromArrayLike : (obj: ArrayLike<number>) => any[]
>CreateIntegerTypedArraysFromArrayLike : (obj: ArrayLike<number>) => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
>obj : ArrayLike<number>
>ArrayLike : ArrayLike<T>
@@ -480,11 +480,11 @@ function CreateIntegerTypedArraysFromArrayLike(obj:ArrayLike<number>) {
>obj : ArrayLike<number>
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
function CreateTypedArraysOf(obj) {
>CreateTypedArraysOf : (obj: any) => any[]
>CreateTypedArraysOf : (obj: any) => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
>obj : any
var typedArrays = [];
@@ -600,11 +600,11 @@ function CreateTypedArraysOf(obj) {
>obj : any
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
function CreateTypedArraysOf2() {
>CreateTypedArraysOf2 : () => any[]
>CreateTypedArraysOf2 : () => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
var typedArrays = [];
>typedArrays : any[]
@@ -737,11 +737,11 @@ function CreateTypedArraysOf2() {
>4 : 4
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
function CreateTypedArraysFromMapFn(obj:ArrayLike<number>, mapFn: (n:number, v:number)=> number) {
>CreateTypedArraysFromMapFn : (obj: ArrayLike<number>, mapFn: (n: number, v: number) => number) => any[]
>CreateTypedArraysFromMapFn : (obj: ArrayLike<number>, mapFn: (n: number, v: number) => number) => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
>obj : ArrayLike<number>
>ArrayLike : ArrayLike<T>
>mapFn : (n: number, v: number) => number
@@ -861,11 +861,11 @@ function CreateTypedArraysFromMapFn(obj:ArrayLike<number>, mapFn: (n:number, v:n
>mapFn : (n: number, v: number) => number
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
function CreateTypedArraysFromThisObj(obj:ArrayLike<number>, mapFn: (n:number, v:number)=> number, thisArg: {}) {
>CreateTypedArraysFromThisObj : (obj: ArrayLike<number>, mapFn: (n: number, v: number) => number, thisArg: {}) => any[]
>CreateTypedArraysFromThisObj : (obj: ArrayLike<number>, mapFn: (n: number, v: number) => number, thisArg: {}) => (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
>obj : ArrayLike<number>
>ArrayLike : ArrayLike<T>
>mapFn : (n: number, v: number) => number
@@ -995,5 +995,5 @@ function CreateTypedArraysFromThisObj(obj:ArrayLike<number>, mapFn: (n:number, v
>thisArg : {}
return typedArrays;
>typedArrays : any[]
>typedArrays : (Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array)[]
}
@@ -0,0 +1,67 @@
// @noImplicitAny: true
declare function cond(): boolean;
function f1() {
let x = []; // Implicit any[] error in some locations
let y = x; // Implicit any[] error
x.push(5);
let z = x;
}
function f2() {
let x; // Implicit any[] error in some locations
x = [];
let y = x; // Implicit any[] error
x.push(5);
let z = x;
}
function f3() {
let x = []; // Implicit any[] error in some locations
x.push(5);
function g() {
x; // Implicit any[] error
}
}
function f4() {
let x;
x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
}
function f5() {
let x = [5, "hello"]; // Non-evolving array
x.push(true); // Error
}
function f6() {
let x;
if (cond()) {
x = [];
x.push(5);
x.push("hello");
}
else {
x = [true]; // Non-evolving array
}
x; // boolean[] | (string | number)[]
x.push(99); // Error
}
function f7() {
let x = []; // x has evolving array value
x.push(5);
let y = x; // y has non-evolving array value
x.push("hello"); // Ok
y.push("hello"); // Error
}
function f8() {
const x = []; // Implicit any[] error in some locations
x.push(5);
function g() {
x; // Implicit any[] error
}
}
+181
View File
@@ -0,0 +1,181 @@
// @strictNullChecks: true
// @noImplicitAny: true
declare function cond(): boolean;
function f1() {
let x = [];
x[0] = 5;
x[1] = "hello";
x[2] = true;
return x; // (string | number | boolean)[]
}
function f2() {
let x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f3() {
let x;
x = [];
x.push(5, "hello");
return x; // (string | number)[]
}
function f4() {
let x = [];
if (cond()) {
x.push(5);
}
else {
x.push("hello");
}
return x; // (string | number)[]
}
function f5() {
let x;
if (cond()) {
x = [];
x.push(5);
}
else {
x = [];
x.push("hello");
}
return x; // (string | number)[]
}
function f6() {
let x;
if (cond()) {
x = 5;
}
else {
x = [];
x.push("hello");
}
return x; // number | string[]
}
function f7() {
let x = null;
if (cond()) {
x = [];
while (cond()) {
x.push("hello");
}
}
return x; // string[] | null
}
function f8() {
let x = [];
x.push(5);
if (cond()) return x; // number[]
x.push("hello");
if (cond()) return x; // (string | number)[]
x.push(true);
return x; // (string | number | boolean)[]
}
function f9() {
let x = [];
if (cond()) {
x.push(5);
return x; // number[]
}
else {
x.push("hello");
return x; // string[]
}
}
function f10() {
let x = [];
if (cond()) {
x.push(true);
x; // boolean[]
}
else {
x.push(5);
x; // number[]
while (cond()) {
x.push("hello");
}
x; // (string | number)[]
}
x.push(99);
return x; // (string | number | boolean)[]
}
function f11() {
let x = [];
if (x.length === 0) { // x.length ok on implicit any[]
x.push("hello");
}
return x;
}
function f12() {
let x;
x = [];
if (x.length === 0) { // x.length ok on implicit any[]
x.push("hello");
}
return x;
}
function f13() {
var x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f14() {
const x = [];
x.push(5);
x.push("hello");
x.push(true);
return x; // (string | number | boolean)[]
}
function f15() {
let x = [];
while (cond()) {
while (cond()) {}
x.push("hello");
}
return x; // string[]
}
function f16() {
let x;
let y;
(x = [], x).push(5);
(x.push("hello"), x).push(true);
((x))[3] = { a: 1 };
return x; // (string | number | boolean | { a: number })[]
}
function f17() {
let x = [];
x.unshift(5);
x.unshift("hello");
x.unshift(true);
return x; // (string | number | boolean)[]
}
function f18() {
let x = [];
x.push(5);
x.unshift("hello");
x[2] = true;
return x; // (string | number | boolean)[]
}
@@ -3,7 +3,7 @@
var x = null; // error at "x"
var x1 = undefined; // error at "x1"
var widenArray = [null, undefined]; // error at "widenArray"
var emptyArray = []; // error at "emptyArray"
var emptyArray = [];
// these should not be error
class AnimalObj {