Skip to content

Commit e4dcd08

Browse files
authored
Merge pull request #13365 from Microsoft/object-spread-removes-class-methods
Omit only class methods from object spreads
2 parents c4a80b2 + 309a361 commit e4dcd08

File tree

4 files changed

+132
-9
lines changed

4 files changed

+132
-9
lines changed

src/compiler/checker.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -6241,7 +6241,7 @@ namespace ts {
62416241
* this function should be called in a left folding style, with left = previous result of getSpreadType
62426242
* and right = the new element to be spread.
62436243
*/
6244-
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): Type {
6244+
function getSpreadType(left: Type, right: Type): Type {
62456245
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
62466246
return anyType;
62476247
}
@@ -6254,10 +6254,10 @@ namespace ts {
62546254
return left;
62556255
}
62566256
if (left.flags & TypeFlags.Union) {
6257-
return mapType(left, t => getSpreadType(t, right, isFromObjectLiteral));
6257+
return mapType(left, t => getSpreadType(t, right));
62586258
}
62596259
if (right.flags & TypeFlags.Union) {
6260-
return mapType(right, t => getSpreadType(left, t, isFromObjectLiteral));
6260+
return mapType(right, t => getSpreadType(left, t));
62616261
}
62626262

62636263
const members = createMap<Symbol>();
@@ -6276,18 +6276,18 @@ namespace ts {
62766276

62776277
for (const rightProp of getPropertiesOfType(right)) {
62786278
// we approximate own properties as non-methods plus methods that are inside the object literal
6279-
const isOwnProperty = !(rightProp.flags & SymbolFlags.Method) || isFromObjectLiteral;
62806279
const isSetterWithoutGetter = rightProp.flags & SymbolFlags.SetAccessor && !(rightProp.flags & SymbolFlags.GetAccessor);
62816280
if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) {
62826281
skippedPrivateMembers[rightProp.name] = true;
62836282
}
6284-
else if (isOwnProperty && !isSetterWithoutGetter) {
6283+
else if (!isClassMethod(rightProp) && !isSetterWithoutGetter) {
62856284
members[rightProp.name] = rightProp;
62866285
}
62876286
}
62886287
for (const leftProp of getPropertiesOfType(left)) {
62896288
if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor)
6290-
|| leftProp.name in skippedPrivateMembers) {
6289+
|| leftProp.name in skippedPrivateMembers
6290+
|| isClassMethod(leftProp)) {
62916291
continue;
62926292
}
62936293
if (leftProp.name in members) {
@@ -6312,6 +6312,10 @@ namespace ts {
63126312
return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
63136313
}
63146314

6315+
function isClassMethod(prop: Symbol) {
6316+
return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent));
6317+
}
6318+
63156319
function createLiteralType(flags: TypeFlags, text: string) {
63166320
const type = <LiteralType>createType(flags);
63176321
type.text = text;
@@ -11658,7 +11662,7 @@ namespace ts {
1165811662
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
1165911663
}
1166011664
if (propertiesArray.length > 0) {
11661-
spread = getSpreadType(spread, createObjectLiteralType(), /*isFromObjectLiteral*/ true);
11665+
spread = getSpreadType(spread, createObjectLiteralType());
1166211666
propertiesArray = [];
1166311667
propertiesTable = createMap<Symbol>();
1166411668
hasComputedStringProperty = false;
@@ -11670,7 +11674,7 @@ namespace ts {
1167011674
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
1167111675
return unknownType;
1167211676
}
11673-
spread = getSpreadType(spread, type, /*isFromObjectLiteral*/ false);
11677+
spread = getSpreadType(spread, type);
1167411678
offset = i + 1;
1167511679
continue;
1167611680
}
@@ -11715,7 +11719,7 @@ namespace ts {
1171511719

1171611720
if (spread !== emptyObjectType) {
1171711721
if (propertiesArray.length > 0) {
11718-
spread = getSpreadType(spread, createObjectLiteralType(), /*isFromObjectLiteral*/ true);
11722+
spread = getSpreadType(spread, createObjectLiteralType());
1171911723
}
1172011724
if (spread.flags & TypeFlags.Object) {
1172111725
// only set the symbol and flags if this is a (fresh) object type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
tests/cases/conformance/types/spread/spreadMethods.ts(7,4): error TS2339: Property 'm' does not exist on type '{ p: number; }'.
2+
tests/cases/conformance/types/spread/spreadMethods.ts(9,5): error TS2339: Property 'm' does not exist on type '{ p: number; }'.
3+
4+
5+
==== tests/cases/conformance/types/spread/spreadMethods.ts (2 errors) ====
6+
class K { p = 12; m() { } }
7+
interface I { p: number, m(): void }
8+
let k = new K()
9+
let sk = { ...k };
10+
let ssk = { ...k, ...k };
11+
sk.p;
12+
sk.m(); // error
13+
~
14+
!!! error TS2339: Property 'm' does not exist on type '{ p: number; }'.
15+
ssk.p;
16+
ssk.m(); // error
17+
~
18+
!!! error TS2339: Property 'm' does not exist on type '{ p: number; }'.
19+
let i: I = { p: 12, m() { } };
20+
let si = { ...i };
21+
let ssi = { ...i, ...i };
22+
si.p;
23+
si.m(); // ok
24+
ssi.p;
25+
ssi.m(); // ok
26+
let o = { p: 12, m() { } };
27+
let so = { ...o };
28+
let sso = { ...o, ...o };
29+
so.p;
30+
so.m(); // ok
31+
sso.p;
32+
sso.m(); // ok
33+
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//// [spreadMethods.ts]
2+
class K { p = 12; m() { } }
3+
interface I { p: number, m(): void }
4+
let k = new K()
5+
let sk = { ...k };
6+
let ssk = { ...k, ...k };
7+
sk.p;
8+
sk.m(); // error
9+
ssk.p;
10+
ssk.m(); // error
11+
let i: I = { p: 12, m() { } };
12+
let si = { ...i };
13+
let ssi = { ...i, ...i };
14+
si.p;
15+
si.m(); // ok
16+
ssi.p;
17+
ssi.m(); // ok
18+
let o = { p: 12, m() { } };
19+
let so = { ...o };
20+
let sso = { ...o, ...o };
21+
so.p;
22+
so.m(); // ok
23+
sso.p;
24+
sso.m(); // ok
25+
26+
27+
//// [spreadMethods.js]
28+
var __assign = (this && this.__assign) || Object.assign || function(t) {
29+
for (var s, i = 1, n = arguments.length; i < n; i++) {
30+
s = arguments[i];
31+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
32+
t[p] = s[p];
33+
}
34+
return t;
35+
};
36+
var K = (function () {
37+
function K() {
38+
this.p = 12;
39+
}
40+
K.prototype.m = function () { };
41+
return K;
42+
}());
43+
var k = new K();
44+
var sk = __assign({}, k);
45+
var ssk = __assign({}, k, k);
46+
sk.p;
47+
sk.m(); // error
48+
ssk.p;
49+
ssk.m(); // error
50+
var i = { p: 12, m: function () { } };
51+
var si = __assign({}, i);
52+
var ssi = __assign({}, i, i);
53+
si.p;
54+
si.m(); // ok
55+
ssi.p;
56+
ssi.m(); // ok
57+
var o = { p: 12, m: function () { } };
58+
var so = __assign({}, o);
59+
var sso = __assign({}, o, o);
60+
so.p;
61+
so.m(); // ok
62+
sso.p;
63+
sso.m(); // ok
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class K { p = 12; m() { } }
2+
interface I { p: number, m(): void }
3+
let k = new K()
4+
let sk = { ...k };
5+
let ssk = { ...k, ...k };
6+
sk.p;
7+
sk.m(); // error
8+
ssk.p;
9+
ssk.m(); // error
10+
let i: I = { p: 12, m() { } };
11+
let si = { ...i };
12+
let ssi = { ...i, ...i };
13+
si.p;
14+
si.m(); // ok
15+
ssi.p;
16+
ssi.m(); // ok
17+
let o = { p: 12, m() { } };
18+
let so = { ...o };
19+
let sso = { ...o, ...o };
20+
so.p;
21+
so.m(); // ok
22+
sso.p;
23+
sso.m(); // ok

0 commit comments

Comments
 (0)