From a1533b695b9ff00ecfc4c9df0d76dba623ff6561 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 12 Dec 2014 10:55:21 -0800 Subject: [PATCH] Implied type of binding pattern with rest element is always an array type --- src/compiler/checker.ts | 51 ++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5e076ebe203..8c2bac87085 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1794,17 +1794,8 @@ module ts { return anyType; } - // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself - // and without regard to its context (i.e. without regard any type annotation or initializer associated with the - // declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] - // and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is - // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring - // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of - // the parameter. - function getTypeFromBindingPattern(pattern: BindingPattern): Type { - if (pattern.kind === SyntaxKind.ArrayBindingPattern) { - return createTupleType(map(pattern.elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e))); - } + // Return the type implied by an object binding pattern + function getTypeFromObjectBindingPattern(pattern: BindingPattern): Type { var members: SymbolTable = {}; forEach(pattern.elements, e => { var flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0); @@ -1816,6 +1807,32 @@ module ts { return createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined); } + // Return the type implied by an array binding pattern + function getTypeFromArrayBindingPattern(pattern: BindingPattern): Type { + var hasSpreadElement: boolean = false; + var elementTypes: Type[] = []; + forEach(pattern.elements, e => { + elementTypes.push(e.kind === SyntaxKind.OmittedExpression || e.dotDotDotToken ? anyType : getTypeFromBindingElement(e)); + if (e.dotDotDotToken) { + hasSpreadElement = true; + } + }); + return !elementTypes.length ? anyArrayType : hasSpreadElement ? createArrayType(getUnionType(elementTypes)) : createTupleType(elementTypes); + } + + // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself + // and without regard to its context (i.e. without regard any type annotation or initializer associated with the + // declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] + // and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is + // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring + // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of + // the parameter. + function getTypeFromBindingPattern(pattern: BindingPattern): Type { + return pattern.kind === SyntaxKind.ObjectBindingPattern + ? getTypeFromObjectBindingPattern(pattern) + : getTypeFromArrayBindingPattern(pattern); + } + // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type // specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it // is a bit more involved. For example: @@ -5336,21 +5353,23 @@ module ts { if (!elements.length) { return createArrayType(undefinedType); } - var contextualType = getContextualType(node); - var isTupleType = (contextualType && contextualTypeIsTupleLikeType(contextualType)) || isAssignmentTarget(node); + var hasSpreadElement: boolean = false; var elementTypes: Type[] = []; forEach(elements, e => { var type = checkExpression(e, contextualMapper); if (e.kind === SyntaxKind.SpreadElementExpression) { elementTypes.push(getIndexTypeOfType(type, IndexKind.Number) || anyType); - isTupleType = false; + hasSpreadElement = true; } else { elementTypes.push(type); } }); - if (isTupleType) { - return createTupleType(elementTypes); + if (!hasSpreadElement) { + var contextualType = getContextualType(node); + if (contextualType && contextualTypeIsTupleLikeType(contextualType) || isAssignmentTarget(node)) { + return createTupleType(elementTypes); + } } return createArrayType(getUnionType(elementTypes)); }