Skip to content

Commit dc8ad6e

Browse files
committedSep 5, 2015
Store binding pattern in contextual type
1 parent 66e3aba commit dc8ad6e

File tree

2 files changed

+52
-44
lines changed

2 files changed

+52
-44
lines changed
 

Diff for: ‎src/compiler/checker.ts

+46-40
Original file line numberDiff line numberDiff line change
@@ -2406,7 +2406,7 @@ namespace ts {
24062406

24072407
// If the declaration specifies a binding pattern, use the type implied by the binding pattern
24082408
if (isBindingPattern(declaration.name)) {
2409-
return getTypeFromBindingPattern(<BindingPattern>declaration.name);
2409+
return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ false);
24102410
}
24112411

24122412
// No type specified and nothing can be inferred
@@ -2421,13 +2421,13 @@ namespace ts {
24212421
return getWidenedType(checkExpressionCached(element.initializer));
24222422
}
24232423
if (isBindingPattern(element.name)) {
2424-
return getTypeFromBindingPattern(<BindingPattern>element.name);
2424+
return getTypeFromBindingPattern(<BindingPattern>element.name, /*includePatternInType*/ false);
24252425
}
24262426
return anyType;
24272427
}
24282428

24292429
// Return the type implied by an object binding pattern
2430-
function getTypeFromObjectBindingPattern(pattern: BindingPattern): Type {
2430+
function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type {
24312431
let members: SymbolTable = {};
24322432
forEach(pattern.elements, e => {
24332433
let flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
@@ -2436,28 +2436,27 @@ namespace ts {
24362436
symbol.type = getTypeFromBindingElement(e);
24372437
members[symbol.name] = symbol;
24382438
});
2439-
return createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined);
2439+
let result = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined);
2440+
if (includePatternInType) {
2441+
result.pattern = pattern;
2442+
}
2443+
return result;
24402444
}
24412445

24422446
// Return the type implied by an array binding pattern
2443-
function getTypeFromArrayBindingPattern(pattern: BindingPattern): Type {
2444-
let hasSpreadElement: boolean = false;
2445-
let elementTypes: Type[] = [];
2446-
forEach(pattern.elements, e => {
2447-
elementTypes.push(e.kind === SyntaxKind.OmittedExpression || e.dotDotDotToken ? anyType : getTypeFromBindingElement(e));
2448-
if (e.dotDotDotToken) {
2449-
hasSpreadElement = true;
2450-
}
2451-
});
2452-
if (!elementTypes.length) {
2447+
function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type {
2448+
let elements = pattern.elements;
2449+
if (elements.length === 0 || elements[elements.length - 1].dotDotDotToken) {
24532450
return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType;
24542451
}
2455-
else if (hasSpreadElement) {
2456-
let unionOfElements = getUnionType(elementTypes);
2457-
return languageVersion >= ScriptTarget.ES6 ? createIterableType(unionOfElements) : createArrayType(unionOfElements);
2458-
}
24592452
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
2460-
return createTupleType(elementTypes);
2453+
let elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e));
2454+
let result = createTupleType(elementTypes);
2455+
if (includePatternInType) {
2456+
result = clone(result);
2457+
result.pattern = pattern;
2458+
}
2459+
return result;
24612460
}
24622461

24632462
// Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself
@@ -2467,10 +2466,10 @@ namespace ts {
24672466
// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring
24682467
// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of
24692468
// the parameter.
2470-
function getTypeFromBindingPattern(pattern: BindingPattern): Type {
2469+
function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean): Type {
24712470
return pattern.kind === SyntaxKind.ObjectBindingPattern
2472-
? getTypeFromObjectBindingPattern(pattern)
2473-
: getTypeFromArrayBindingPattern(pattern);
2471+
? getTypeFromObjectBindingPattern(pattern, includePatternInType)
2472+
: getTypeFromArrayBindingPattern(pattern, includePatternInType);
24742473
}
24752474

24762475
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
@@ -6606,18 +6605,12 @@ namespace ts {
66066605
}
66076606
}
66086607
if (isBindingPattern(declaration.name)) {
6609-
return createImpliedType(getTypeFromBindingPattern(<BindingPattern>declaration.name));
6608+
return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ true);
66106609
}
66116610
}
66126611
return undefined;
66136612
}
66146613

6615-
function createImpliedType(type: Type): Type {
6616-
var result = clone(type);
6617-
result.flags |= TypeFlags.ImpliedType;
6618-
return result;
6619-
}
6620-
66216614
function getContextualTypeForReturnExpression(node: Expression): Type {
66226615
let func = getContainingFunction(node);
66236616
if (func && !func.asteriskToken) {
@@ -7044,17 +7037,28 @@ namespace ts {
70447037
// If array literal is actually a destructuring pattern, mark it as an implied type. We do this such
70457038
// that we get the same behavior for "var [x, y] = []" and "[x, y] = []".
70467039
if (inDestructuringPattern && elementTypes.length) {
7047-
return createImpliedType(createTupleType(elementTypes));
7040+
let type = clone(createTupleType(elementTypes));
7041+
type.pattern = node;
7042+
return type;
70487043
}
70497044
let contextualType = getContextualType(node);
7050-
let contextualTupleLikeType = contextualType && contextualTypeIsTupleLikeType(contextualType) ? contextualType : undefined;
7051-
if (contextualTupleLikeType) {
7052-
// If array literal is contextually typed by the implied type of a binding pattern, pad the resulting
7053-
// tuple type with elements from the binding tuple type to make the lengths equal.
7054-
if (contextualTupleLikeType.flags & TypeFlags.Tuple && contextualTupleLikeType.flags & TypeFlags.ImpliedType) {
7055-
let contextualElementTypes = (<TupleType>contextualTupleLikeType).elementTypes;
7056-
for (let i = elementTypes.length; i < contextualElementTypes.length; i++) {
7057-
elementTypes.push(contextualElementTypes[i]);
7045+
if (contextualType && contextualTypeIsTupleLikeType(contextualType)) {
7046+
let pattern = contextualType.pattern;
7047+
// If array literal is contextually typed by a binding pattern or an assignment pattern,
7048+
// pad the resulting tuple type to make the lengths equal.
7049+
if (pattern && pattern.kind === SyntaxKind.ArrayBindingPattern) {
7050+
let bindingElements = (<BindingPattern>pattern).elements;
7051+
for (let i = elementTypes.length; i < bindingElements.length; i++) {
7052+
let hasDefaultValue = bindingElements[i].initializer;
7053+
elementTypes.push(hasDefaultValue ? (<TupleType>contextualType).elementTypes[i] : undefinedType);
7054+
}
7055+
}
7056+
else if (pattern && pattern.kind === SyntaxKind.ArrayLiteralExpression) {
7057+
let assignmentElements = (<ArrayLiteralExpression>pattern).elements;
7058+
for (let i = elementTypes.length; i < assignmentElements.length; i++) {
7059+
let hasDefaultValue = assignmentElements[i].kind === SyntaxKind.BinaryExpression &&
7060+
(<BinaryExpression>assignmentElements[i]).operatorToken.kind === SyntaxKind.EqualsToken;
7061+
elementTypes.push(hasDefaultValue ? (<TupleType>contextualType).elementTypes[i] : undefinedType);
70587062
}
70597063
}
70607064
if (elementTypes.length) {
@@ -7129,6 +7133,8 @@ namespace ts {
71297133
let propertiesTable: SymbolTable = {};
71307134
let propertiesArray: Symbol[] = [];
71317135
let contextualType = getContextualType(node);
7136+
let contextualTypeHasPattern = contextualType && contextualType.pattern &&
7137+
contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern;
71327138
let typeFlags: TypeFlags = 0;
71337139

71347140
for (let memberDecl of node.properties) {
@@ -7151,7 +7157,7 @@ namespace ts {
71517157
let prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name);
71527158
// If object literal is contextually typed by the implied type of a binding pattern, and if the
71537159
// binding pattern specifies a default value for the property, make the property optional.
7154-
if (contextualType && contextualType.flags & TypeFlags.ImpliedType) {
7160+
if (contextualTypeHasPattern) {
71557161
let impliedProp = getPropertyOfType(contextualType, member.name);
71567162
if (impliedProp) {
71577163
prop.flags |= impliedProp.flags & SymbolFlags.Optional;
@@ -7185,7 +7191,7 @@ namespace ts {
71857191

71867192
// If object literal is contextually typed by the implied type of a binding pattern, augment the result
71877193
// type with those properties for which the binding pattern specifies a default value.
7188-
if (contextualType && contextualType.flags & TypeFlags.ImpliedType) {
7194+
if (contextualTypeHasPattern) {
71897195
for (let prop of getPropertiesOfType(contextualType)) {
71907196
if (prop.flags & SymbolFlags.Optional && !hasProperty(propertiesTable, prop.name)) {
71917197
propertiesTable[prop.name] = prop;

Diff for: ‎src/compiler/types.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1795,7 +1795,6 @@ namespace ts {
17951795
/* @internal */
17961796
ContainsAnyFunctionType = 0x00800000, // Type is or contains object literal type
17971797
ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6
1798-
ImpliedType = 0x02000000, // Type implied by object binding pattern
17991798

18001799
/* @internal */
18011800
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
@@ -1812,11 +1811,14 @@ namespace ts {
18121811
PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType
18131812
}
18141813

1814+
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
1815+
18151816
// Properties common to all types
18161817
export interface Type {
1817-
flags: TypeFlags; // Flags
1818-
/* @internal */ id: number; // Unique ID
1819-
symbol?: Symbol; // Symbol associated with type (if any)
1818+
flags: TypeFlags; // Flags
1819+
/* @internal */ id: number; // Unique ID
1820+
symbol?: Symbol; // Symbol associated with type (if any)
1821+
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
18201822
}
18211823

18221824
/* @internal */

0 commit comments

Comments
 (0)