Skip to content

Commit fc1f6e3

Browse files
authored
Merge pull request #12564 from Microsoft/rest-of-untyped-binding-pattern-is-any
Rest of untyped binding pattern is { [s: string]: any }
2 parents 09f158d + 7722631 commit fc1f6e3

File tree

8 files changed

+58
-5
lines changed

8 files changed

+58
-5
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3319,14 +3319,19 @@ namespace ts {
33193319
// Return the type implied by an object binding pattern
33203320
function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
33213321
const members = createMap<Symbol>();
3322+
let stringIndexInfo: IndexInfo;
33223323
let hasComputedProperties = false;
33233324
forEach(pattern.elements, e => {
33243325
const name = e.propertyName || <Identifier>e.name;
3325-
if (isComputedNonLiteralName(name) || e.dotDotDotToken) {
3326-
// do not include computed properties or rests in the implied type
3326+
if (isComputedNonLiteralName(name)) {
3327+
// do not include computed properties in the implied type
33273328
hasComputedProperties = true;
33283329
return;
33293330
}
3331+
if (e.dotDotDotToken) {
3332+
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
3333+
return;
3334+
}
33303335

33313336
const text = getTextOfPropertyName(name);
33323337
const flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
@@ -3335,7 +3340,7 @@ namespace ts {
33353340
symbol.bindingElement = e;
33363341
members[symbol.name] = symbol;
33373342
});
3338-
const result = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined);
3343+
const result = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, undefined);
33393344
if (includePatternInType) {
33403345
result.pattern = pattern;
33413346
}
@@ -11447,7 +11452,8 @@ namespace ts {
1144711452
if (impliedProp) {
1144811453
prop.flags |= impliedProp.flags & SymbolFlags.Optional;
1144911454
}
11450-
else if (!compilerOptions.suppressExcessPropertyErrors) {
11455+
11456+
else if (!compilerOptions.suppressExcessPropertyErrors && !getIndexInfoOfType(contextualType, IndexKind.String)) {
1145111457
error(memberDecl.name, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1,
1145211458
symbolToString(member), typeToString(contextualType));
1145311459
}

tests/baselines/reference/objectRest.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ let computed = 'b';
3636
let computed2 = 'a';
3737
var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
3838
({ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o);
39+
40+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];
3941

4042

4143
//// [objectRest.js]
@@ -76,4 +78,8 @@ let computed = 'b';
7678
let computed2 = 'a';
7779
var _g = computed, stillNotGreat = o[_g], _h = computed2, soSo = o[_h], o = __rest(o, [typeof _g === "symbol" ? _g : _g + "", typeof _h === "symbol" ? _h : _h + ""]);
7880
(_j = computed, stillNotGreat = o[_j], _k = computed2, soSo = o[_k], o = __rest(o, [typeof _j === "symbol" ? _j : _j + "", typeof _k === "symbol" ? _k : _k + ""]));
81+
var noContextualType = (_a) => {
82+
var { aNumber = 12 } = _a, notEmptyObject = __rest(_a, ["aNumber"]);
83+
return aNumber + notEmptyObject['anythingGoes'];
84+
};
7985
var _d, _f, _j, _k;

tests/baselines/reference/objectRest.symbols

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,10 @@ var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
169169
>o : Symbol(o, Decl(objectRest.ts, 0, 3), Decl(objectRest.ts, 35, 51))
170170
>o : Symbol(o, Decl(objectRest.ts, 0, 3), Decl(objectRest.ts, 35, 51))
171171

172+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];
173+
>noContextualType : Symbol(noContextualType, Decl(objectRest.ts, 38, 3))
174+
>aNumber : Symbol(aNumber, Decl(objectRest.ts, 38, 25))
175+
>notEmptyObject : Symbol(notEmptyObject, Decl(objectRest.ts, 38, 39))
176+
>aNumber : Symbol(aNumber, Decl(objectRest.ts, 38, 25))
177+
>notEmptyObject : Symbol(notEmptyObject, Decl(objectRest.ts, 38, 39))
178+

tests/baselines/reference/objectRest.types

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,15 @@ var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
195195
>o : { a: number; b: string; }
196196
>o : { a: number; b: string; }
197197

198+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];
199+
>noContextualType : ({aNumber, ...notEmptyObject}: { [x: string]: any; aNumber?: number; }) => any
200+
>({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'] : ({aNumber, ...notEmptyObject}: { [x: string]: any; aNumber?: number; }) => any
201+
>aNumber : number
202+
>12 : 12
203+
>notEmptyObject : { [x: string]: any; }
204+
>aNumber + notEmptyObject['anythingGoes'] : any
205+
>aNumber : number
206+
>notEmptyObject['anythingGoes'] : any
207+
>notEmptyObject : { [x: string]: any; }
208+
>'anythingGoes' : "anythingGoes"
209+

tests/baselines/reference/objectRestNegative.errors.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(6,10): error TS2322: Ty
33
Types of property 'a' are incompatible.
44
Type 'number' is not assignable to type 'string'.
55
tests/cases/conformance/types/rest/objectRestNegative.ts(9,31): error TS2462: A rest element must be last in a destructuring pattern
6+
tests/cases/conformance/types/rest/objectRestNegative.ts(11,30): error TS7008: Member 'x' implicitly has an 'any' type.
7+
tests/cases/conformance/types/rest/objectRestNegative.ts(11,33): error TS7008: Member 'y' implicitly has an 'any' type.
68
tests/cases/conformance/types/rest/objectRestNegative.ts(12,17): error TS2700: Rest types may only be created from object types.
79
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.
10+
tests/cases/conformance/types/rest/objectRestNegative.ts(19,90): error TS2339: Property 'anythingGoes' does not exist on type '{ [x: string]: any; }'.
811

912

10-
==== tests/cases/conformance/types/rest/objectRestNegative.ts (5 errors) ====
13+
==== tests/cases/conformance/types/rest/objectRestNegative.ts (8 errors) ====
1114
let o = { a: 1, b: 'no' };
1215
var { ...mustBeLast, a } = o;
1316
~~~~~~~~~~
@@ -27,6 +30,10 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(17,9): error TS2701: Th
2730
!!! error TS2462: A rest element must be last in a destructuring pattern
2831
}
2932
function generic<T extends { x, y }>(t: T) {
33+
~~
34+
!!! error TS7008: Member 'x' implicitly has an 'any' type.
35+
~
36+
!!! error TS7008: Member 'y' implicitly has an 'any' type.
3037
let { x, ...rest } = t;
3138
~~~~
3239
!!! error TS2700: Rest types may only be created from object types.
@@ -37,4 +44,8 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(17,9): error TS2701: Th
3744
({a, ...rest.b + rest.b} = o);
3845
~~~~~~~~~~~~~~~
3946
!!! error TS2701: The target of an object rest assignment must be a variable or a property access.
47+
48+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;
49+
~~~~~~~~~~~~
50+
!!! error TS2339: Property 'anythingGoes' does not exist on type '{ [x: string]: any; }'.
4051

tests/baselines/reference/objectRestNegative.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ function generic<T extends { x, y }>(t: T) {
1616

1717
let rest: { b: string }
1818
({a, ...rest.b + rest.b} = o);
19+
20+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;
1921

2022

2123
//// [objectRestNegative.js]
@@ -42,3 +44,7 @@ function generic(t) {
4244
}
4345
var rest;
4446
(a = o.a, o, rest.b + rest.b = __rest(o, ["a"]));
47+
var noContextualType = function (_a) {
48+
var _b = _a.aNumber, aNumber = _b === void 0 ? 12 : _b, notEmptyObject = __rest(_a, ["aNumber"]);
49+
return aNumber + notEmptyObject.anythingGoes;
50+
};

tests/cases/conformance/types/rest/objectRest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ let computed = 'b';
3636
let computed2 = 'a';
3737
var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
3838
({ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o);
39+
40+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];

tests/cases/conformance/types/rest/objectRestNegative.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @noImplicitAny: true
12
let o = { a: 1, b: 'no' };
23
var { ...mustBeLast, a } = o;
34

@@ -15,3 +16,5 @@ function generic<T extends { x, y }>(t: T) {
1516

1617
let rest: { b: string }
1718
({a, ...rest.b + rest.b} = o);
19+
20+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;

0 commit comments

Comments
 (0)