Skip to content

Commit 363c514

Browse files
authored
Merge pull request #14635 from Microsoft/fixIntersectionMethodOverrides
Fix intersection method overrides
2 parents 18f0d85 + dd84d7c commit 363c514

File tree

8 files changed

+336
-26
lines changed

8 files changed

+336
-26
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5129,7 +5129,8 @@ namespace ts {
51295129
const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0;
51305130
// Flags we want to propagate to the result if they exist in all source symbols
51315131
let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
5132-
let checkFlags = CheckFlags.SyntheticProperty;
5132+
let syntheticFlag = CheckFlags.SyntheticMethod;
5133+
let checkFlags = 0;
51335134
for (const current of types) {
51345135
const type = getApparentType(current);
51355136
if (type !== unknownType) {
@@ -5148,6 +5149,9 @@ namespace ts {
51485149
(modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) |
51495150
(modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) |
51505151
(modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0);
5152+
if (!isMethodLike(prop)) {
5153+
syntheticFlag = CheckFlags.SyntheticProperty;
5154+
}
51515155
}
51525156
else if (isUnion) {
51535157
checkFlags |= CheckFlags.Partial;
@@ -5177,7 +5181,7 @@ namespace ts {
51775181
propTypes.push(type);
51785182
}
51795183
const result = createSymbol(SymbolFlags.Property | commonFlags, name);
5180-
result.checkFlags = checkFlags;
5184+
result.checkFlags = syntheticFlag | checkFlags;
51815185
result.containingType = containingType;
51825186
result.declarations = declarations;
51835187
result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes);
@@ -8630,7 +8634,7 @@ namespace ts {
86308634
// Invoke the callback for each underlying property symbol of the given symbol and return the first
86318635
// value that isn't undefined.
86328636
function forEachProperty<T>(prop: Symbol, callback: (p: Symbol) => T): T {
8633-
if (getCheckFlags(prop) & CheckFlags.SyntheticProperty) {
8637+
if (getCheckFlags(prop) & CheckFlags.Synthetic) {
86348638
for (const t of (<TransientSymbol>prop).containingType.types) {
86358639
const p = getPropertyOfType(t, prop.name);
86368640
const result = p && forEachProperty(p, callback);
@@ -13112,7 +13116,7 @@ namespace ts {
1311213116
const flags = getCombinedModifierFlags(s.valueDeclaration);
1311313117
return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
1311413118
}
13115-
if (getCheckFlags(s) & CheckFlags.SyntheticProperty) {
13119+
if (getCheckFlags(s) & CheckFlags.Synthetic) {
1311613120
const checkFlags = (<TransientSymbol>s).checkFlags;
1311713121
const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private :
1311813122
checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public :
@@ -13130,6 +13134,10 @@ namespace ts {
1313013134
return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : 0;
1313113135
}
1313213136

13137+
function isMethodLike(symbol: Symbol) {
13138+
return !!(symbol.flags & SymbolFlags.Method || getCheckFlags(symbol) & CheckFlags.SyntheticMethod);
13139+
}
13140+
1313313141
/**
1313413142
* Check whether the requested property access is valid.
1313513143
* Returns true if node is a valid property access, and false otherwise.
@@ -13159,11 +13167,11 @@ namespace ts {
1315913167
// where this references the constructor function object of a derived class,
1316013168
// a super property access is permitted and must specify a public static member function of the base class.
1316113169
if (languageVersion < ScriptTarget.ES2015) {
13162-
const propKind = getDeclarationKindFromSymbol(prop);
13163-
if (propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature) {
13164-
// `prop` refers to a *property* declared in the super class
13165-
// rather than a *method*, so it does not satisfy the above criteria.
13166-
13170+
const hasNonMethodDeclaration = forEachProperty(prop, p => {
13171+
const propKind = getDeclarationKindFromSymbol(p);
13172+
return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature;
13173+
});
13174+
if (hasNonMethodDeclaration) {
1316713175
error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword);
1316813176
return false;
1316913177
}
@@ -19697,7 +19705,7 @@ namespace ts {
1969719705
else {
1969819706
// derived overrides base.
1969919707
const derivedDeclarationFlags = getDeclarationModifierFlagsFromSymbol(derived);
19700-
if ((baseDeclarationFlags & ModifierFlags.Private) || (derivedDeclarationFlags & ModifierFlags.Private)) {
19708+
if (baseDeclarationFlags & ModifierFlags.Private || derivedDeclarationFlags & ModifierFlags.Private) {
1970119709
// either base or derived property is private - not override, skip it
1970219710
continue;
1970319711
}
@@ -19707,28 +19715,24 @@ namespace ts {
1970719715
continue;
1970819716
}
1970919717

19710-
if ((base.flags & derived.flags & SymbolFlags.Method) || ((base.flags & SymbolFlags.PropertyOrAccessor) && (derived.flags & SymbolFlags.PropertyOrAccessor))) {
19718+
if (isMethodLike(base) && isMethodLike(derived) || base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) {
1971119719
// method is overridden with method or property/accessor is overridden with property/accessor - correct case
1971219720
continue;
1971319721
}
1971419722

1971519723
let errorMessage: DiagnosticMessage;
19716-
if (base.flags & SymbolFlags.Method) {
19724+
if (isMethodLike(base)) {
1971719725
if (derived.flags & SymbolFlags.Accessor) {
1971819726
errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor;
1971919727
}
1972019728
else {
19721-
Debug.assert((derived.flags & SymbolFlags.Property) !== 0);
1972219729
errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property;
1972319730
}
1972419731
}
1972519732
else if (base.flags & SymbolFlags.Property) {
19726-
Debug.assert((derived.flags & SymbolFlags.Method) !== 0);
1972719733
errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function;
1972819734
}
1972919735
else {
19730-
Debug.assert((base.flags & SymbolFlags.Accessor) !== 0);
19731-
Debug.assert((derived.flags & SymbolFlags.Method) !== 0);
1973219736
errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function;
1973319737
}
1973419738

@@ -21387,7 +21391,7 @@ namespace ts {
2138721391
}
2138821392

2138921393
function getRootSymbols(symbol: Symbol): Symbol[] {
21390-
if (getCheckFlags(symbol) & CheckFlags.SyntheticProperty) {
21394+
if (getCheckFlags(symbol) & CheckFlags.Synthetic) {
2139121395
const symbols: Symbol[] = [];
2139221396
const name = symbol.name;
2139321397
forEach(getSymbolLinks(symbol).containingType.types, t => {

src/compiler/types.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2825,13 +2825,15 @@ namespace ts {
28252825
export const enum CheckFlags {
28262826
Instantiated = 1 << 0, // Instantiated symbol
28272827
SyntheticProperty = 1 << 1, // Property in union or intersection type
2828-
Readonly = 1 << 2, // Readonly transient symbol
2829-
Partial = 1 << 3, // Synthetic property present in some but not all constituents
2830-
HasNonUniformType = 1 << 4, // Synthetic property with non-uniform type in constituents
2831-
ContainsPublic = 1 << 5, // Synthetic property with public constituent(s)
2832-
ContainsProtected = 1 << 6, // Synthetic property with protected constituent(s)
2833-
ContainsPrivate = 1 << 7, // Synthetic property with private constituent(s)
2834-
ContainsStatic = 1 << 8, // Synthetic property with static constituent(s)
2828+
SyntheticMethod = 1 << 2, // Method in union or intersection type
2829+
Readonly = 1 << 3, // Readonly transient symbol
2830+
Partial = 1 << 4, // Synthetic property present in some but not all constituents
2831+
HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents
2832+
ContainsPublic = 1 << 6, // Synthetic property with public constituent(s)
2833+
ContainsProtected = 1 << 7, // Synthetic property with protected constituent(s)
2834+
ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s)
2835+
ContainsStatic = 1 << 9, // Synthetic property with static constituent(s)
2836+
Synthetic = SyntheticProperty | SyntheticMethod
28352837
}
28362838

28372839
/* @internal */

src/services/findAllReferences.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ namespace ts.FindAllReferences {
335335

336336
// if this symbol is visible from its parent container, e.g. exported, then bail out
337337
// if symbol correspond to the union property - bail out
338-
if (symbol.parent || (symbol.flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty)) {
338+
if (symbol.parent || (symbol.flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.Synthetic)) {
339339
return undefined;
340340
}
341341

src/services/symbolDisplay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ namespace ts.SymbolDisplay {
5252
if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement;
5353

5454
if (flags & SymbolFlags.Property) {
55-
if (flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty) {
55+
if (flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.Synthetic) {
5656
// If union property is result of union of non method (property/accessors/variables), it is labeled as property
5757
const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => {
5858
const rootSymbolFlags = rootSymbol.getFlags();
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//// [overrideBaseIntersectionMethod.ts]
2+
3+
// Repro from #14615
4+
5+
type Constructor<T> = new (...args: any[]) => T;
6+
7+
const WithLocation = <T extends Constructor<Point>>(Base: T) => class extends Base {
8+
getLocation(): [number, number] {
9+
const [x,y] = super.getLocation();
10+
return [this.x | x, this.y | y];
11+
}
12+
}
13+
14+
class Point {
15+
constructor(public x: number, public y: number) { }
16+
getLocation(): [number, number] {
17+
return [0,0];
18+
}
19+
}
20+
21+
class Foo extends WithLocation(Point) {
22+
calculate() {
23+
return this.x + this.y;
24+
}
25+
getLocation() {
26+
return super.getLocation()
27+
}
28+
whereAmI() {
29+
return this.getLocation();
30+
}
31+
}
32+
33+
34+
//// [overrideBaseIntersectionMethod.js]
35+
// Repro from #14615
36+
"use strict";
37+
var __extends = (this && this.__extends) || (function () {
38+
var extendStatics = Object.setPrototypeOf ||
39+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
40+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
41+
return function (d, b) {
42+
extendStatics(d, b);
43+
function __() { this.constructor = d; }
44+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
45+
};
46+
})();
47+
var WithLocation = function (Base) { return (function (_super) {
48+
__extends(class_1, _super);
49+
function class_1() {
50+
return _super !== null && _super.apply(this, arguments) || this;
51+
}
52+
class_1.prototype.getLocation = function () {
53+
var _a = _super.prototype.getLocation.call(this), x = _a[0], y = _a[1];
54+
return [this.x | x, this.y | y];
55+
};
56+
return class_1;
57+
}(Base)); };
58+
var Point = (function () {
59+
function Point(x, y) {
60+
this.x = x;
61+
this.y = y;
62+
}
63+
Point.prototype.getLocation = function () {
64+
return [0, 0];
65+
};
66+
return Point;
67+
}());
68+
var Foo = (function (_super) {
69+
__extends(Foo, _super);
70+
function Foo() {
71+
return _super !== null && _super.apply(this, arguments) || this;
72+
}
73+
Foo.prototype.calculate = function () {
74+
return this.x + this.y;
75+
};
76+
Foo.prototype.getLocation = function () {
77+
return _super.prototype.getLocation.call(this);
78+
};
79+
Foo.prototype.whereAmI = function () {
80+
return this.getLocation();
81+
};
82+
return Foo;
83+
}(WithLocation(Point)));
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
=== tests/cases/compiler/overrideBaseIntersectionMethod.ts ===
2+
3+
// Repro from #14615
4+
5+
type Constructor<T> = new (...args: any[]) => T;
6+
>Constructor : Symbol(Constructor, Decl(overrideBaseIntersectionMethod.ts, 0, 0))
7+
>T : Symbol(T, Decl(overrideBaseIntersectionMethod.ts, 3, 17))
8+
>args : Symbol(args, Decl(overrideBaseIntersectionMethod.ts, 3, 27))
9+
>T : Symbol(T, Decl(overrideBaseIntersectionMethod.ts, 3, 17))
10+
11+
const WithLocation = <T extends Constructor<Point>>(Base: T) => class extends Base {
12+
>WithLocation : Symbol(WithLocation, Decl(overrideBaseIntersectionMethod.ts, 5, 5))
13+
>T : Symbol(T, Decl(overrideBaseIntersectionMethod.ts, 5, 22))
14+
>Constructor : Symbol(Constructor, Decl(overrideBaseIntersectionMethod.ts, 0, 0))
15+
>Point : Symbol(Point, Decl(overrideBaseIntersectionMethod.ts, 10, 1))
16+
>Base : Symbol(Base, Decl(overrideBaseIntersectionMethod.ts, 5, 52))
17+
>T : Symbol(T, Decl(overrideBaseIntersectionMethod.ts, 5, 22))
18+
>Base : Symbol(Base, Decl(overrideBaseIntersectionMethod.ts, 5, 52))
19+
20+
getLocation(): [number, number] {
21+
>getLocation : Symbol((Anonymous class).getLocation, Decl(overrideBaseIntersectionMethod.ts, 5, 84))
22+
23+
const [x,y] = super.getLocation();
24+
>x : Symbol(x, Decl(overrideBaseIntersectionMethod.ts, 7, 11))
25+
>y : Symbol(y, Decl(overrideBaseIntersectionMethod.ts, 7, 13))
26+
>super.getLocation : Symbol(Point.getLocation, Decl(overrideBaseIntersectionMethod.ts, 13, 53))
27+
>super : Symbol(Point, Decl(overrideBaseIntersectionMethod.ts, 10, 1))
28+
>getLocation : Symbol(Point.getLocation, Decl(overrideBaseIntersectionMethod.ts, 13, 53))
29+
30+
return [this.x | x, this.y | y];
31+
>this.x : Symbol(Point.x, Decl(overrideBaseIntersectionMethod.ts, 13, 14))
32+
>this : Symbol((Anonymous class), Decl(overrideBaseIntersectionMethod.ts, 5, 63))
33+
>x : Symbol(Point.x, Decl(overrideBaseIntersectionMethod.ts, 13, 14))
34+
>x : Symbol(x, Decl(overrideBaseIntersectionMethod.ts, 7, 11))
35+
>this.y : Symbol(Point.y, Decl(overrideBaseIntersectionMethod.ts, 13, 31))
36+
>this : Symbol((Anonymous class), Decl(overrideBaseIntersectionMethod.ts, 5, 63))
37+
>y : Symbol(Point.y, Decl(overrideBaseIntersectionMethod.ts, 13, 31))
38+
>y : Symbol(y, Decl(overrideBaseIntersectionMethod.ts, 7, 13))
39+
}
40+
}
41+
42+
class Point {
43+
>Point : Symbol(Point, Decl(overrideBaseIntersectionMethod.ts, 10, 1))
44+
45+
constructor(public x: number, public y: number) { }
46+
>x : Symbol(Point.x, Decl(overrideBaseIntersectionMethod.ts, 13, 14))
47+
>y : Symbol(Point.y, Decl(overrideBaseIntersectionMethod.ts, 13, 31))
48+
49+
getLocation(): [number, number] {
50+
>getLocation : Symbol(Point.getLocation, Decl(overrideBaseIntersectionMethod.ts, 13, 53))
51+
52+
return [0,0];
53+
}
54+
}
55+
56+
class Foo extends WithLocation(Point) {
57+
>Foo : Symbol(Foo, Decl(overrideBaseIntersectionMethod.ts, 17, 1))
58+
>WithLocation : Symbol(WithLocation, Decl(overrideBaseIntersectionMethod.ts, 5, 5))
59+
>Point : Symbol(Point, Decl(overrideBaseIntersectionMethod.ts, 10, 1))
60+
61+
calculate() {
62+
>calculate : Symbol(Foo.calculate, Decl(overrideBaseIntersectionMethod.ts, 19, 39))
63+
64+
return this.x + this.y;
65+
>this.x : Symbol(Point.x, Decl(overrideBaseIntersectionMethod.ts, 13, 14))
66+
>this : Symbol(Foo, Decl(overrideBaseIntersectionMethod.ts, 17, 1))
67+
>x : Symbol(Point.x, Decl(overrideBaseIntersectionMethod.ts, 13, 14))
68+
>this.y : Symbol(Point.y, Decl(overrideBaseIntersectionMethod.ts, 13, 31))
69+
>this : Symbol(Foo, Decl(overrideBaseIntersectionMethod.ts, 17, 1))
70+
>y : Symbol(Point.y, Decl(overrideBaseIntersectionMethod.ts, 13, 31))
71+
}
72+
getLocation() {
73+
>getLocation : Symbol(Foo.getLocation, Decl(overrideBaseIntersectionMethod.ts, 22, 3))
74+
75+
return super.getLocation()
76+
>super.getLocation : Symbol(getLocation, Decl(overrideBaseIntersectionMethod.ts, 5, 84), Decl(overrideBaseIntersectionMethod.ts, 13, 53))
77+
>getLocation : Symbol(getLocation, Decl(overrideBaseIntersectionMethod.ts, 5, 84), Decl(overrideBaseIntersectionMethod.ts, 13, 53))
78+
}
79+
whereAmI() {
80+
>whereAmI : Symbol(Foo.whereAmI, Decl(overrideBaseIntersectionMethod.ts, 25, 3))
81+
82+
return this.getLocation();
83+
>this.getLocation : Symbol(Foo.getLocation, Decl(overrideBaseIntersectionMethod.ts, 22, 3))
84+
>this : Symbol(Foo, Decl(overrideBaseIntersectionMethod.ts, 17, 1))
85+
>getLocation : Symbol(Foo.getLocation, Decl(overrideBaseIntersectionMethod.ts, 22, 3))
86+
}
87+
}
88+

0 commit comments

Comments
 (0)