Merge pull request #13365 from Microsoft/object-spread-removes-class-methods

Omit only class methods from object spreads
This commit is contained in:
Nathan Shively-Sanders 2017-01-09 11:10:33 -08:00 committed by GitHub
commit e4dcd0803b
4 changed files with 132 additions and 9 deletions

View file

@ -6241,7 +6241,7 @@ namespace ts {
* this function should be called in a left folding style, with left = previous result of getSpreadType
* and right = the new element to be spread.
*/
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): Type {
function getSpreadType(left: Type, right: Type): Type {
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
return anyType;
}
@ -6254,10 +6254,10 @@ namespace ts {
return left;
}
if (left.flags & TypeFlags.Union) {
return mapType(left, t => getSpreadType(t, right, isFromObjectLiteral));
return mapType(left, t => getSpreadType(t, right));
}
if (right.flags & TypeFlags.Union) {
return mapType(right, t => getSpreadType(left, t, isFromObjectLiteral));
return mapType(right, t => getSpreadType(left, t));
}
const members = createMap<Symbol>();
@ -6276,18 +6276,18 @@ namespace ts {
for (const rightProp of getPropertiesOfType(right)) {
// we approximate own properties as non-methods plus methods that are inside the object literal
const isOwnProperty = !(rightProp.flags & SymbolFlags.Method) || isFromObjectLiteral;
const isSetterWithoutGetter = rightProp.flags & SymbolFlags.SetAccessor && !(rightProp.flags & SymbolFlags.GetAccessor);
if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) {
skippedPrivateMembers[rightProp.name] = true;
}
else if (isOwnProperty && !isSetterWithoutGetter) {
else if (!isClassMethod(rightProp) && !isSetterWithoutGetter) {
members[rightProp.name] = rightProp;
}
}
for (const leftProp of getPropertiesOfType(left)) {
if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor)
|| leftProp.name in skippedPrivateMembers) {
|| leftProp.name in skippedPrivateMembers
|| isClassMethod(leftProp)) {
continue;
}
if (leftProp.name in members) {
@ -6312,6 +6312,10 @@ namespace ts {
return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
}
function isClassMethod(prop: Symbol) {
return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent));
}
function createLiteralType(flags: TypeFlags, text: string) {
const type = <LiteralType>createType(flags);
type.text = text;
@ -11658,7 +11662,7 @@ namespace ts {
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
}
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType(), /*isFromObjectLiteral*/ true);
spread = getSpreadType(spread, createObjectLiteralType());
propertiesArray = [];
propertiesTable = createMap<Symbol>();
hasComputedStringProperty = false;
@ -11670,7 +11674,7 @@ namespace ts {
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
return unknownType;
}
spread = getSpreadType(spread, type, /*isFromObjectLiteral*/ false);
spread = getSpreadType(spread, type);
offset = i + 1;
continue;
}
@ -11715,7 +11719,7 @@ namespace ts {
if (spread !== emptyObjectType) {
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType(), /*isFromObjectLiteral*/ true);
spread = getSpreadType(spread, createObjectLiteralType());
}
if (spread.flags & TypeFlags.Object) {
// only set the symbol and flags if this is a (fresh) object type

View file

@ -0,0 +1,33 @@
tests/cases/conformance/types/spread/spreadMethods.ts(7,4): error TS2339: Property 'm' does not exist on type '{ p: number; }'.
tests/cases/conformance/types/spread/spreadMethods.ts(9,5): error TS2339: Property 'm' does not exist on type '{ p: number; }'.
==== tests/cases/conformance/types/spread/spreadMethods.ts (2 errors) ====
class K { p = 12; m() { } }
interface I { p: number, m(): void }
let k = new K()
let sk = { ...k };
let ssk = { ...k, ...k };
sk.p;
sk.m(); // error
~
!!! error TS2339: Property 'm' does not exist on type '{ p: number; }'.
ssk.p;
ssk.m(); // error
~
!!! error TS2339: Property 'm' does not exist on type '{ p: number; }'.
let i: I = { p: 12, m() { } };
let si = { ...i };
let ssi = { ...i, ...i };
si.p;
si.m(); // ok
ssi.p;
ssi.m(); // ok
let o = { p: 12, m() { } };
let so = { ...o };
let sso = { ...o, ...o };
so.p;
so.m(); // ok
sso.p;
sso.m(); // ok

View file

@ -0,0 +1,63 @@
//// [spreadMethods.ts]
class K { p = 12; m() { } }
interface I { p: number, m(): void }
let k = new K()
let sk = { ...k };
let ssk = { ...k, ...k };
sk.p;
sk.m(); // error
ssk.p;
ssk.m(); // error
let i: I = { p: 12, m() { } };
let si = { ...i };
let ssi = { ...i, ...i };
si.p;
si.m(); // ok
ssi.p;
ssi.m(); // ok
let o = { p: 12, m() { } };
let so = { ...o };
let sso = { ...o, ...o };
so.p;
so.m(); // ok
sso.p;
sso.m(); // ok
//// [spreadMethods.js]
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
var K = (function () {
function K() {
this.p = 12;
}
K.prototype.m = function () { };
return K;
}());
var k = new K();
var sk = __assign({}, k);
var ssk = __assign({}, k, k);
sk.p;
sk.m(); // error
ssk.p;
ssk.m(); // error
var i = { p: 12, m: function () { } };
var si = __assign({}, i);
var ssi = __assign({}, i, i);
si.p;
si.m(); // ok
ssi.p;
ssi.m(); // ok
var o = { p: 12, m: function () { } };
var so = __assign({}, o);
var sso = __assign({}, o, o);
so.p;
so.m(); // ok
sso.p;
sso.m(); // ok

View file

@ -0,0 +1,23 @@
class K { p = 12; m() { } }
interface I { p: number, m(): void }
let k = new K()
let sk = { ...k };
let ssk = { ...k, ...k };
sk.p;
sk.m(); // error
ssk.p;
ssk.m(); // error
let i: I = { p: 12, m() { } };
let si = { ...i };
let ssi = { ...i, ...i };
si.p;
si.m(); // ok
ssi.p;
ssi.m(); // ok
let o = { p: 12, m() { } };
let so = { ...o };
let sso = { ...o, ...o };
so.p;
so.m(); // ok
sso.p;
sso.m(); // ok