Merge pull request #13365 from Microsoft/object-spread-removes-class-methods
Omit only class methods from object spreads
This commit is contained in:
commit
e4dcd0803b
4 changed files with 132 additions and 9 deletions
|
@ -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
|
||||
|
|
33
tests/baselines/reference/spreadMethods.errors.txt
Normal file
33
tests/baselines/reference/spreadMethods.errors.txt
Normal 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
|
||||
|
63
tests/baselines/reference/spreadMethods.js
Normal file
63
tests/baselines/reference/spreadMethods.js
Normal 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
|
23
tests/cases/conformance/types/spread/spreadMethods.ts
Normal file
23
tests/cases/conformance/types/spread/spreadMethods.ts
Normal 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
|
Loading…
Reference in a new issue