diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 918acb410aa..e12f65b7291 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7335,45 +7335,52 @@ namespace ts { return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : emptyUnionType; } - function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { - const type = checkExpressionCached(node.right); - const parent = node.parent; - if (parent.kind === SyntaxKind.ArrayLiteralExpression || parent.kind === SyntaxKind.PropertyAssignment) { - const assignedType = getAssignedType(node); - return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), type]); + function getTypeWithDefault(type: Type, defaultExpression: Expression) { + if (defaultExpression) { + const defaultType = checkExpressionCached(defaultExpression); + return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]); } return type; } - function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { - const arrayLikeType = getAssignedType(node); - const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false); - const propName = "" + indexOf(node.elements, element); - return isTupleLikeType(arrayLikeType) && getTypeOfPropertyOfType(arrayLikeType, propName) || elementType; - } - - function getAssignedTypeOfSpreadElement(node: SpreadElementExpression): Type { - const arrayLikeType = getAssignedType(node.parent); - const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false); - return createArrayType(elementType); - } - - function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type { - const objectType = getAssignedType(node.parent); - const text = getTextOfPropertyName(node.name); - return getTypeOfPropertyOfType(objectType, text) || - isNumericLiteralName(text) && getIndexTypeOfType(objectType, IndexKind.Number) || - getIndexTypeOfType(objectType, IndexKind.String) || + function getTypeOfDestructuredProperty(type: Type, name: Identifier | LiteralExpression | ComputedPropertyName) { + const text = getTextOfPropertyName(name); + return getTypeOfPropertyOfType(type, text) || + isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) || + getIndexTypeOfType(type, IndexKind.String) || unknownType; } + function getTypeOfDestructuredArrayElement(type: Type, index: number) { + return isTupleLikeType(type) && getTypeOfPropertyOfType(type, "" + index) || + checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false) || + unknownType; + } + + function getTypeOfDestructuredSpreadElement(type: Type) { + return createArrayType(checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false) || unknownType); + } + + function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { + return node.parent.kind === SyntaxKind.ArrayLiteralExpression || node.parent.kind === SyntaxKind.PropertyAssignment ? + getTypeWithDefault(getAssignedType(node), node.right) : + checkExpressionCached(node.right); + } + + function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { + return getTypeOfDestructuredArrayElement(getAssignedType(node), indexOf(node.elements, element)); + } + + function getAssignedTypeOfSpreadElement(node: SpreadElementExpression): Type { + return getTypeOfDestructuredSpreadElement(getAssignedType(node.parent)); + } + + function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type { + return getTypeOfDestructuredProperty(getAssignedType(node.parent), node.name); + } + function getAssignedTypeOfShorthandPropertyAssignment(node: ShorthandPropertyAssignment): Type { - if (node.objectAssignmentInitializer) { - const defaultType = checkExpressionCached(node.objectAssignmentInitializer); - const assignedType = getAssignedTypeOfPropertyAssignment(node); - return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), defaultType]); - } - return getAssignedTypeOfPropertyAssignment(node); + return getTypeWithDefault(getAssignedTypeOfPropertyAssignment(node), node.objectAssignmentInitializer); } function getAssignedType(node: Expression): Type { @@ -7382,7 +7389,7 @@ namespace ts { case SyntaxKind.ForInStatement: return stringType; case SyntaxKind.ForOfStatement: - return checkRightHandSideOfForOf((parent).expression); + return checkRightHandSideOfForOf((parent).expression) || unknownType; case SyntaxKind.BinaryExpression: return getAssignedTypeOfBinaryExpression(parent); case SyntaxKind.ArrayLiteralExpression: @@ -7397,6 +7404,36 @@ namespace ts { return unknownType; } + function getInitialTypeOfBindingElement(node: BindingElement): Type { + const pattern = node.parent; + const parentType = getInitialType(pattern.parent); + const type = pattern.kind === SyntaxKind.ObjectBindingPattern ? + getTypeOfDestructuredProperty(parentType, node.propertyName || node.name) : + !node.dotDotDotToken ? + getTypeOfDestructuredArrayElement(parentType, indexOf(pattern.elements, node)) : + getTypeOfDestructuredSpreadElement(parentType); + return getTypeWithDefault(type, node.initializer); + } + + function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) { + if (node.initializer) { + return checkExpressionCached(node.initializer); + } + if (node.parent.parent.kind === SyntaxKind.ForInStatement) { + return stringType; + } + if (node.parent.parent.kind === SyntaxKind.ForOfStatement) { + return checkRightHandSideOfForOf((node.parent.parent).expression) || unknownType; + } + return unknownType; + } + + function getInitialType(node: VariableDeclaration | BindingElement) { + return node.kind === SyntaxKind.VariableDeclaration ? + getInitialTypeOfVariableDeclaration(node) : + getInitialTypeOfBindingElement(node); + } + function getNarrowedTypeOfReference(type: Type, reference: Node) { if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) { return type; @@ -7446,20 +7483,12 @@ namespace ts { } } - function getTypeAtVariableDeclaration(node: VariableDeclaration | BindingElement) { - if (reference.kind === SyntaxKind.Identifier && getResolvedSymbol(reference) === getSymbolOfNode(node)) { - if (node.initializer) { - return getAssignmentReducedType(declaredType, checkExpressionCached(node.initializer)); - } - return declaredType; // !!! TODO - } - return undefined; - } - function getTypeAtFlowAssignment(flow: FlowAssignment) { const node = flow.node; - if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { - return getTypeAtVariableDeclaration(node); + if ((node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) && + reference.kind === SyntaxKind.Identifier && + getResolvedSymbol(reference) === getSymbolOfNode(node)) { + return getAssignmentReducedType(declaredType, getInitialType(node)); } // If the node is not a variable declaration or binding element, it is an identifier // or a dotted name that is the target of an assignment. If we have a match, reduce @@ -14091,23 +14120,21 @@ namespace ts { if (isTypeAny(inputType)) { return inputType; } - if (languageVersion >= ScriptTarget.ES6) { return checkElementTypeOfIterable(inputType, errorNode); } - if (allowStringInput) { return checkElementTypeOfArrayOrString(inputType, errorNode); } - if (isArrayLikeType(inputType)) { const indexType = getIndexTypeOfType(inputType, IndexKind.Number); if (indexType) { return indexType; } } - - error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); + if (errorNode) { + error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); + } return unknownType; }