From 641948fb0cec0c27a3764b34ff947dd0f6d3b3be Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 16 Nov 2016 13:38:25 -0800 Subject: [PATCH 1/2] Test assignability checking of object rests --- .../reference/objectRestNegative.errors.txt | 21 +++++++++++++++---- .../baselines/reference/objectRestNegative.js | 9 ++++++++ .../reference/objectSpreadNoTransform.js | 2 +- .../reference/objectSpreadNoTransform.symbols | 2 +- .../reference/objectSpreadNoTransform.types | 4 ++-- .../types/rest/objectRestNegative.ts | 6 ++++++ .../types/spread/objectSpreadNoTransform.ts | 2 +- 7 files changed, 37 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/objectRestNegative.errors.txt b/tests/baselines/reference/objectRestNegative.errors.txt index 793a7d31fc8..345e99723bd 100644 --- a/tests/baselines/reference/objectRestNegative.errors.txt +++ b/tests/baselines/reference/objectRestNegative.errors.txt @@ -1,14 +1,27 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(2,10): error TS2462: A rest element must be last in a destructuring pattern -tests/cases/conformance/types/rest/objectRestNegative.ts(3,31): error TS2462: A rest element must be last in a destructuring pattern -tests/cases/conformance/types/rest/objectRestNegative.ts(6,17): error TS2700: Rest types may only be created from object types. -tests/cases/conformance/types/rest/objectRestNegative.ts(11,9): error TS2701: The target of an object rest assignment must be a variable or a property access. +tests/cases/conformance/types/rest/objectRestNegative.ts(6,10): error TS2322: Type '{ a: number; }' is not assignable to type '{ a: string; }'. + Types of property 'a' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/types/rest/objectRestNegative.ts(9,31): error TS2462: A rest element must be last in a destructuring pattern +tests/cases/conformance/types/rest/objectRestNegative.ts(12,17): error TS2700: Rest types may only be created from object types. +tests/cases/conformance/types/rest/objectRestNegative.ts(17,9): error TS2701: The target of an object rest assignment must be a variable or a property access. -==== tests/cases/conformance/types/rest/objectRestNegative.ts (4 errors) ==== +==== tests/cases/conformance/types/rest/objectRestNegative.ts (5 errors) ==== let o = { a: 1, b: 'no' }; var { ...mustBeLast, a } = o; ~~~~~~~~~~ !!! error TS2462: A rest element must be last in a destructuring pattern + + var b: string; + let notAssignable: { a: string }; + ({ b, ...notAssignable } = o); + ~~~~~~~~~~~~~ +!!! error TS2322: Type '{ a: number; }' is not assignable to type '{ a: string; }'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + function stillMustBeLast({ ...mustBeLast, a }: { a: number, b: string }): void { ~~~~~~~~~~ !!! error TS2462: A rest element must be last in a destructuring pattern diff --git a/tests/baselines/reference/objectRestNegative.js b/tests/baselines/reference/objectRestNegative.js index a2da998cf8a..f8c5b72ada5 100644 --- a/tests/baselines/reference/objectRestNegative.js +++ b/tests/baselines/reference/objectRestNegative.js @@ -1,6 +1,12 @@ //// [objectRestNegative.ts] let o = { a: 1, b: 'no' }; var { ...mustBeLast, a } = o; + +var b: string; +let notAssignable: { a: string }; +({ b, ...notAssignable } = o); + + function stillMustBeLast({ ...mustBeLast, a }: { a: number, b: string }): void { } function generic(t: T) { @@ -24,6 +30,9 @@ var __rest = (this && this.__rest) || function (s, e) { }; var o = { a: 1, b: 'no' }; var a = o.a; +var b; +var notAssignable; +(b = o.b, o, notAssignable = __rest(o, ["b"])); function stillMustBeLast(_a) { var a = _a.a; } diff --git a/tests/baselines/reference/objectSpreadNoTransform.js b/tests/baselines/reference/objectSpreadNoTransform.js index 3442d086409..5ad9c01e599 100644 --- a/tests/baselines/reference/objectSpreadNoTransform.js +++ b/tests/baselines/reference/objectSpreadNoTransform.js @@ -2,7 +2,7 @@ const y = { a: 'yes', b: 'no' }; const o = { x: 1, ...y }; var b; -var rest; +var rest: any; ({ b, ...rest } = o); diff --git a/tests/baselines/reference/objectSpreadNoTransform.symbols b/tests/baselines/reference/objectSpreadNoTransform.symbols index 78423330dbb..b2062697cdd 100644 --- a/tests/baselines/reference/objectSpreadNoTransform.symbols +++ b/tests/baselines/reference/objectSpreadNoTransform.symbols @@ -12,7 +12,7 @@ const o = { x: 1, ...y }; var b; >b : Symbol(b, Decl(objectSpreadNoTransform.ts, 2, 3)) -var rest; +var rest: any; >rest : Symbol(rest, Decl(objectSpreadNoTransform.ts, 3, 3)) ({ b, ...rest } = o); diff --git a/tests/baselines/reference/objectSpreadNoTransform.types b/tests/baselines/reference/objectSpreadNoTransform.types index ae89a3a7993..bb09fd418c2 100644 --- a/tests/baselines/reference/objectSpreadNoTransform.types +++ b/tests/baselines/reference/objectSpreadNoTransform.types @@ -17,7 +17,7 @@ const o = { x: 1, ...y }; var b; >b : any -var rest; +var rest: any; >rest : any ({ b, ...rest } = o); @@ -25,6 +25,6 @@ var rest; >{ b, ...rest } = o : { a: string; b: string; x: number; } >{ b, ...rest } : any >b : any ->rest : undefined +>rest : any >o : { a: string; b: string; x: number; } diff --git a/tests/cases/conformance/types/rest/objectRestNegative.ts b/tests/cases/conformance/types/rest/objectRestNegative.ts index 75cbe9a55e4..c224c8cd30e 100644 --- a/tests/cases/conformance/types/rest/objectRestNegative.ts +++ b/tests/cases/conformance/types/rest/objectRestNegative.ts @@ -1,5 +1,11 @@ let o = { a: 1, b: 'no' }; var { ...mustBeLast, a } = o; + +var b: string; +let notAssignable: { a: string }; +({ b, ...notAssignable } = o); + + function stillMustBeLast({ ...mustBeLast, a }: { a: number, b: string }): void { } function generic(t: T) { diff --git a/tests/cases/conformance/types/spread/objectSpreadNoTransform.ts b/tests/cases/conformance/types/spread/objectSpreadNoTransform.ts index 36c75e70887..86bcb7eeee5 100644 --- a/tests/cases/conformance/types/spread/objectSpreadNoTransform.ts +++ b/tests/cases/conformance/types/spread/objectSpreadNoTransform.ts @@ -2,5 +2,5 @@ const y = { a: 'yes', b: 'no' }; const o = { x: 1, ...y }; var b; -var rest; +var rest: any; ({ b, ...rest } = o); From 074014e03989edf37bbf270e26846fca88da1aee Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 16 Nov 2016 13:39:00 -0800 Subject: [PATCH 2/2] Check assignability: rest destructuring assignment --- src/compiler/checker.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7132aedb324..b2555b7d21d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14188,12 +14188,13 @@ namespace ts { function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type): Type { const properties = node.properties; for (const p of properties) { - checkObjectLiteralDestructuringPropertyAssignment(sourceType, p); + checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, properties); } return sourceType; } - function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike) { + /** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */ + function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: ObjectLiteralElementLike[]) { if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) { const name = (property).name; if (name.kind === SyntaxKind.ComputedPropertyName) { @@ -14223,7 +14224,14 @@ namespace ts { } } else if (property.kind === SyntaxKind.SpreadAssignment) { - checkReferenceExpression(property.expression, Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access); + const nonRestNames: PropertyName[] = []; + if (allProperties) { + for (let i = 0; i < allProperties.length - 1; i++) { + nonRestNames.push(allProperties[i].name); + } + } + const type = getRestType(objectLiteralType, nonRestNames, objectLiteralType.symbol); + return checkDestructuringAssignment(property.expression, type); } else { error(property, Diagnostics.Property_assignment_expected); @@ -14321,7 +14329,10 @@ namespace ts { function checkReferenceAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type { const targetType = checkExpression(target, contextualMapper); - if (checkReferenceExpression(target, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)) { + const error = target.parent.kind === SyntaxKind.SpreadAssignment ? + Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access : + Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access; + if (checkReferenceExpression(target, error)) { checkTypeAssignableTo(sourceType, targetType, target, /*headMessage*/ undefined); } return sourceType;