Skip to content

Commit 0862c3c

Browse files
committed
Accessors can override synthetic properties
This is the simplest fix -- I'm going to check whether it's worthwhile to make it stricter next. Fixes #41347
1 parent c3e132d commit 0862c3c

File tree

5 files changed

+330
-1
lines changed

5 files changed

+330
-1
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36244,8 +36244,9 @@ namespace ts {
3624436244
// property/accessor is overridden with property/accessor
3624536245
if (baseDeclarationFlags & ModifierFlags.Abstract && !(base.valueDeclaration && isPropertyDeclaration(base.valueDeclaration) && base.valueDeclaration.initializer)
3624636246
|| base.valueDeclaration && base.valueDeclaration.parent.kind === SyntaxKind.InterfaceDeclaration
36247+
|| getCheckFlags(base) & CheckFlags.Synthetic
3624736248
|| derived.valueDeclaration && isBinaryExpression(derived.valueDeclaration)) {
36248-
// when the base property is abstract or from an interface, base/derived flags don't need to match
36249+
// when the base property is abstract, from an interface or from an intersection, base/derived flags don't need to match
3624936250
// same when the derived property is from an assignment
3625036251
continue;
3625136252
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//// [accessorsOverrideProperty8.ts]
2+
// #41347, based on microsoft/rushstack
3+
4+
// Mixin utilities
5+
export type Constructor<T = {}> = new (...args: any[]) => T;
6+
export type PropertiesOf<T> = { [K in keyof T]: T[K] };
7+
8+
interface IApiItemConstructor extends Constructor<ApiItem>, PropertiesOf<typeof ApiItem> {}
9+
10+
// Base class
11+
class ApiItem {
12+
public get members(): ReadonlyArray<ApiItem> {
13+
return [];
14+
}
15+
}
16+
17+
// Normal subclass
18+
class ApiEnumMember extends ApiItem {
19+
}
20+
21+
// Mixin base class
22+
interface ApiItemContainerMixin extends ApiItem {
23+
readonly members: ReadonlyArray<ApiItem>;
24+
}
25+
26+
function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
27+
baseClass: TBaseClass
28+
): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) {
29+
abstract class MixedClass extends baseClass implements ApiItemContainerMixin {
30+
public constructor(...args: any[]) {
31+
super(...args);
32+
}
33+
34+
public get members(): ReadonlyArray<ApiItem> {
35+
return [];
36+
}
37+
}
38+
39+
return MixedClass;
40+
}
41+
42+
// Subclass inheriting from mixin
43+
export class ApiEnum extends ApiItemContainerMixin(ApiItem) {
44+
// This worked prior to TypeScript 4.0:
45+
public get members(): ReadonlyArray<ApiEnumMember> {
46+
return [];
47+
}
48+
}
49+
50+
51+
//// [accessorsOverrideProperty8.js]
52+
// #41347, based on microsoft/rushstack
53+
// Base class
54+
class ApiItem {
55+
get members() {
56+
return [];
57+
}
58+
}
59+
// Normal subclass
60+
class ApiEnumMember extends ApiItem {
61+
}
62+
function ApiItemContainerMixin(baseClass) {
63+
class MixedClass extends baseClass {
64+
constructor(...args) {
65+
super(...args);
66+
}
67+
get members() {
68+
return [];
69+
}
70+
}
71+
return MixedClass;
72+
}
73+
// Subclass inheriting from mixin
74+
export class ApiEnum extends ApiItemContainerMixin(ApiItem) {
75+
// This worked prior to TypeScript 4.0:
76+
get members() {
77+
return [];
78+
}
79+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty8.ts ===
2+
// #41347, based on microsoft/rushstack
3+
4+
// Mixin utilities
5+
export type Constructor<T = {}> = new (...args: any[]) => T;
6+
>Constructor : Symbol(Constructor, Decl(accessorsOverrideProperty8.ts, 0, 0))
7+
>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 3, 24))
8+
>args : Symbol(args, Decl(accessorsOverrideProperty8.ts, 3, 39))
9+
>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 3, 24))
10+
11+
export type PropertiesOf<T> = { [K in keyof T]: T[K] };
12+
>PropertiesOf : Symbol(PropertiesOf, Decl(accessorsOverrideProperty8.ts, 3, 60))
13+
>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 4, 25))
14+
>K : Symbol(K, Decl(accessorsOverrideProperty8.ts, 4, 33))
15+
>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 4, 25))
16+
>T : Symbol(T, Decl(accessorsOverrideProperty8.ts, 4, 25))
17+
>K : Symbol(K, Decl(accessorsOverrideProperty8.ts, 4, 33))
18+
19+
interface IApiItemConstructor extends Constructor<ApiItem>, PropertiesOf<typeof ApiItem> {}
20+
>IApiItemConstructor : Symbol(IApiItemConstructor, Decl(accessorsOverrideProperty8.ts, 4, 55))
21+
>Constructor : Symbol(Constructor, Decl(accessorsOverrideProperty8.ts, 0, 0))
22+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
23+
>PropertiesOf : Symbol(PropertiesOf, Decl(accessorsOverrideProperty8.ts, 3, 60))
24+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
25+
26+
// Base class
27+
class ApiItem {
28+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
29+
30+
public get members(): ReadonlyArray<ApiItem> {
31+
>members : Symbol(ApiItem.members, Decl(accessorsOverrideProperty8.ts, 9, 15))
32+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --))
33+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
34+
35+
return [];
36+
}
37+
}
38+
39+
// Normal subclass
40+
class ApiEnumMember extends ApiItem {
41+
>ApiEnumMember : Symbol(ApiEnumMember, Decl(accessorsOverrideProperty8.ts, 13, 1))
42+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
43+
}
44+
45+
// Mixin base class
46+
interface ApiItemContainerMixin extends ApiItem {
47+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty8.ts, 22, 1), Decl(accessorsOverrideProperty8.ts, 17, 1))
48+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
49+
50+
readonly members: ReadonlyArray<ApiItem>;
51+
>members : Symbol(ApiItemContainerMixin.members, Decl(accessorsOverrideProperty8.ts, 20, 49))
52+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --))
53+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
54+
}
55+
56+
function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
57+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty8.ts, 22, 1), Decl(accessorsOverrideProperty8.ts, 17, 1))
58+
>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty8.ts, 24, 31))
59+
>IApiItemConstructor : Symbol(IApiItemConstructor, Decl(accessorsOverrideProperty8.ts, 4, 55))
60+
61+
baseClass: TBaseClass
62+
>baseClass : Symbol(baseClass, Decl(accessorsOverrideProperty8.ts, 24, 71))
63+
>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty8.ts, 24, 31))
64+
65+
): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) {
66+
>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty8.ts, 24, 31))
67+
>args : Symbol(args, Decl(accessorsOverrideProperty8.ts, 26, 22))
68+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty8.ts, 22, 1), Decl(accessorsOverrideProperty8.ts, 17, 1))
69+
70+
abstract class MixedClass extends baseClass implements ApiItemContainerMixin {
71+
>MixedClass : Symbol(MixedClass, Decl(accessorsOverrideProperty8.ts, 26, 65))
72+
>baseClass : Symbol(baseClass, Decl(accessorsOverrideProperty8.ts, 24, 71))
73+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty8.ts, 22, 1), Decl(accessorsOverrideProperty8.ts, 17, 1))
74+
75+
public constructor(...args: any[]) {
76+
>args : Symbol(args, Decl(accessorsOverrideProperty8.ts, 28, 23))
77+
78+
super(...args);
79+
>super : Symbol(TBaseClass, Decl(accessorsOverrideProperty8.ts, 24, 31))
80+
>args : Symbol(args, Decl(accessorsOverrideProperty8.ts, 28, 23))
81+
}
82+
83+
public get members(): ReadonlyArray<ApiItem> {
84+
>members : Symbol(MixedClass.members, Decl(accessorsOverrideProperty8.ts, 30, 5))
85+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --))
86+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
87+
88+
return [];
89+
}
90+
}
91+
92+
return MixedClass;
93+
>MixedClass : Symbol(MixedClass, Decl(accessorsOverrideProperty8.ts, 26, 65))
94+
}
95+
96+
// Subclass inheriting from mixin
97+
export class ApiEnum extends ApiItemContainerMixin(ApiItem) {
98+
>ApiEnum : Symbol(ApiEnum, Decl(accessorsOverrideProperty8.ts, 38, 1))
99+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty8.ts, 22, 1), Decl(accessorsOverrideProperty8.ts, 17, 1))
100+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty8.ts, 6, 91))
101+
102+
// This worked prior to TypeScript 4.0:
103+
public get members(): ReadonlyArray<ApiEnumMember> {
104+
>members : Symbol(ApiEnum.members, Decl(accessorsOverrideProperty8.ts, 41, 61))
105+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --))
106+
>ApiEnumMember : Symbol(ApiEnumMember, Decl(accessorsOverrideProperty8.ts, 13, 1))
107+
108+
return [];
109+
}
110+
}
111+
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty8.ts ===
2+
// #41347, based on microsoft/rushstack
3+
4+
// Mixin utilities
5+
export type Constructor<T = {}> = new (...args: any[]) => T;
6+
>Constructor : Constructor<T>
7+
>args : any[]
8+
9+
export type PropertiesOf<T> = { [K in keyof T]: T[K] };
10+
>PropertiesOf : PropertiesOf<T>
11+
12+
interface IApiItemConstructor extends Constructor<ApiItem>, PropertiesOf<typeof ApiItem> {}
13+
>ApiItem : typeof ApiItem
14+
15+
// Base class
16+
class ApiItem {
17+
>ApiItem : ApiItem
18+
19+
public get members(): ReadonlyArray<ApiItem> {
20+
>members : readonly ApiItem[]
21+
22+
return [];
23+
>[] : never[]
24+
}
25+
}
26+
27+
// Normal subclass
28+
class ApiEnumMember extends ApiItem {
29+
>ApiEnumMember : ApiEnumMember
30+
>ApiItem : ApiItem
31+
}
32+
33+
// Mixin base class
34+
interface ApiItemContainerMixin extends ApiItem {
35+
readonly members: ReadonlyArray<ApiItem>;
36+
>members : readonly ApiItem[]
37+
}
38+
39+
function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
40+
>ApiItemContainerMixin : <TBaseClass extends IApiItemConstructor>(baseClass: TBaseClass) => TBaseClass & (new (...args: any[]) => ApiItemContainerMixin)
41+
42+
baseClass: TBaseClass
43+
>baseClass : TBaseClass
44+
45+
): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) {
46+
>args : any[]
47+
48+
abstract class MixedClass extends baseClass implements ApiItemContainerMixin {
49+
>MixedClass : MixedClass
50+
>baseClass : ApiItem
51+
52+
public constructor(...args: any[]) {
53+
>args : any[]
54+
55+
super(...args);
56+
>super(...args) : void
57+
>super : TBaseClass
58+
>...args : any
59+
>args : any[]
60+
}
61+
62+
public get members(): ReadonlyArray<ApiItem> {
63+
>members : readonly ApiItem[]
64+
65+
return [];
66+
>[] : never[]
67+
}
68+
}
69+
70+
return MixedClass;
71+
>MixedClass : ((abstract new (...args: any[]) => MixedClass) & { prototype: ApiItemContainerMixin<any>.MixedClass; }) & TBaseClass
72+
}
73+
74+
// Subclass inheriting from mixin
75+
export class ApiEnum extends ApiItemContainerMixin(ApiItem) {
76+
>ApiEnum : ApiEnum
77+
>ApiItemContainerMixin(ApiItem) : ApiItem & ApiItemContainerMixin
78+
>ApiItemContainerMixin : <TBaseClass extends IApiItemConstructor>(baseClass: TBaseClass) => TBaseClass & (new (...args: any[]) => ApiItemContainerMixin)
79+
>ApiItem : typeof ApiItem
80+
81+
// This worked prior to TypeScript 4.0:
82+
public get members(): ReadonlyArray<ApiEnumMember> {
83+
>members : readonly ApiEnumMember[]
84+
85+
return [];
86+
>[] : never[]
87+
}
88+
}
89+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// @strict: true
2+
// @target: es2017
3+
// #41347, based on microsoft/rushstack
4+
5+
// Mixin utilities
6+
export type Constructor<T = {}> = new (...args: any[]) => T;
7+
export type PropertiesOf<T> = { [K in keyof T]: T[K] };
8+
9+
interface IApiItemConstructor extends Constructor<ApiItem>, PropertiesOf<typeof ApiItem> {}
10+
11+
// Base class
12+
class ApiItem {
13+
public get members(): ReadonlyArray<ApiItem> {
14+
return [];
15+
}
16+
}
17+
18+
// Normal subclass
19+
class ApiEnumMember extends ApiItem {
20+
}
21+
22+
// Mixin base class
23+
interface ApiItemContainerMixin extends ApiItem {
24+
readonly members: ReadonlyArray<ApiItem>;
25+
}
26+
27+
function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
28+
baseClass: TBaseClass
29+
): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) {
30+
abstract class MixedClass extends baseClass implements ApiItemContainerMixin {
31+
public constructor(...args: any[]) {
32+
super(...args);
33+
}
34+
35+
public get members(): ReadonlyArray<ApiItem> {
36+
return [];
37+
}
38+
}
39+
40+
return MixedClass;
41+
}
42+
43+
// Subclass inheriting from mixin
44+
export class ApiEnum extends ApiItemContainerMixin(ApiItem) {
45+
// This worked prior to TypeScript 4.0:
46+
public get members(): ReadonlyArray<ApiEnumMember> {
47+
return [];
48+
}
49+
}

0 commit comments

Comments
 (0)