Skip to content

Commit 80112a8

Browse files
committed
Also handle synthetic properties more laxly
Originally from #42635, but this version is simpler.
1 parent 63fd359 commit 80112a8

File tree

5 files changed

+339
-4
lines changed

5 files changed

+339
-4
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6899,7 +6899,7 @@ namespace ts {
68996899
...!length(baseTypes) ? [] : [factory.createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
69006900
...!length(implementsExpressions) ? [] : [factory.createHeritageClause(SyntaxKind.ImplementsKeyword, implementsExpressions)]
69016901
];
6902-
const symbolProps = getNonInterhitedProperties(classType, baseTypes, getPropertiesOfType(classType));
6902+
const symbolProps = getNonInheritedProperties(classType, baseTypes, getPropertiesOfType(classType));
69036903
const publicSymbolProps = filter(symbolProps, s => {
69046904
// `valueDeclaration` could be undefined if inherited from
69056905
// a union/intersection base type, but inherited properties
@@ -36225,10 +36225,12 @@ namespace ts {
3622536225
const derivedPropertyFlags = derived.flags & SymbolFlags.PropertyOrAccessor;
3622636226
if (basePropertyFlags && derivedPropertyFlags) {
3622736227
// property/accessor is overridden with property/accessor
36228-
if (baseDeclarationFlags & ModifierFlags.Abstract && !(base.valueDeclaration && isPropertyDeclaration(base.valueDeclaration) && base.valueDeclaration.initializer)
36229-
|| !base.declarations?.some(decl => isClassLike(decl.parent))
36228+
if ((getCheckFlags(base) & CheckFlags.Synthetic
36229+
? base.declarations?.some(d => isPropertyAbstractOrNonClass(d, baseDeclarationFlags))
36230+
: base.declarations?.every(d => isPropertyAbstractOrNonClass(d, baseDeclarationFlags)))
3623036231
|| derived.valueDeclaration && isBinaryExpression(derived.valueDeclaration)) {
3623136232
// when the base property is abstract or not from a class, base/derived flags don't need to match
36233+
// for intersection properties, this must be true of *any* of the declarations, for others it must be true of *all*
3623236234
// same when the derived property is from an assignment
3623336235
continue;
3623436236
}
@@ -36286,7 +36288,12 @@ namespace ts {
3628636288
}
3628736289
}
3628836290

36289-
function getNonInterhitedProperties(type: InterfaceType, baseTypes: BaseType[], properties: Symbol[]) {
36291+
function isPropertyAbstractOrNonClass(declaration: Declaration, baseDeclarationFlags: ModifierFlags) {
36292+
return baseDeclarationFlags & ModifierFlags.Abstract && (!isPropertyDeclaration(declaration) || !declaration.initializer)
36293+
|| !isClassLike(declaration.parent);
36294+
}
36295+
36296+
function getNonInheritedProperties(type: InterfaceType, baseTypes: BaseType[], properties: Symbol[]) {
3629036297
if (!length(baseTypes)) {
3629136298
return properties;
3629236299
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//// [accessorsOverrideProperty9.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+
//// [accessorsOverrideProperty9.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/accessorsOverrideProperty9.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(accessorsOverrideProperty9.ts, 0, 0))
7+
>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 3, 24))
8+
>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 3, 39))
9+
>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 3, 24))
10+
11+
export type PropertiesOf<T> = { [K in keyof T]: T[K] };
12+
>PropertiesOf : Symbol(PropertiesOf, Decl(accessorsOverrideProperty9.ts, 3, 60))
13+
>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 4, 25))
14+
>K : Symbol(K, Decl(accessorsOverrideProperty9.ts, 4, 33))
15+
>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 4, 25))
16+
>T : Symbol(T, Decl(accessorsOverrideProperty9.ts, 4, 25))
17+
>K : Symbol(K, Decl(accessorsOverrideProperty9.ts, 4, 33))
18+
19+
interface IApiItemConstructor extends Constructor<ApiItem>, PropertiesOf<typeof ApiItem> {}
20+
>IApiItemConstructor : Symbol(IApiItemConstructor, Decl(accessorsOverrideProperty9.ts, 4, 55))
21+
>Constructor : Symbol(Constructor, Decl(accessorsOverrideProperty9.ts, 0, 0))
22+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91))
23+
>PropertiesOf : Symbol(PropertiesOf, Decl(accessorsOverrideProperty9.ts, 3, 60))
24+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91))
25+
26+
// Base class
27+
class ApiItem {
28+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91))
29+
30+
public get members(): ReadonlyArray<ApiItem> {
31+
>members : Symbol(ApiItem.members, Decl(accessorsOverrideProperty9.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(accessorsOverrideProperty9.ts, 6, 91))
34+
35+
return [];
36+
}
37+
}
38+
39+
// Normal subclass
40+
class ApiEnumMember extends ApiItem {
41+
>ApiEnumMember : Symbol(ApiEnumMember, Decl(accessorsOverrideProperty9.ts, 13, 1))
42+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91))
43+
}
44+
45+
// Mixin base class
46+
interface ApiItemContainerMixin extends ApiItem {
47+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1))
48+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91))
49+
50+
readonly members: ReadonlyArray<ApiItem>;
51+
>members : Symbol(ApiItemContainerMixin.members, Decl(accessorsOverrideProperty9.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(accessorsOverrideProperty9.ts, 6, 91))
54+
}
55+
56+
function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
57+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1))
58+
>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31))
59+
>IApiItemConstructor : Symbol(IApiItemConstructor, Decl(accessorsOverrideProperty9.ts, 4, 55))
60+
61+
baseClass: TBaseClass
62+
>baseClass : Symbol(baseClass, Decl(accessorsOverrideProperty9.ts, 24, 71))
63+
>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31))
64+
65+
): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) {
66+
>TBaseClass : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31))
67+
>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 26, 22))
68+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1))
69+
70+
abstract class MixedClass extends baseClass implements ApiItemContainerMixin {
71+
>MixedClass : Symbol(MixedClass, Decl(accessorsOverrideProperty9.ts, 26, 65))
72+
>baseClass : Symbol(baseClass, Decl(accessorsOverrideProperty9.ts, 24, 71))
73+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1))
74+
75+
public constructor(...args: any[]) {
76+
>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 28, 23))
77+
78+
super(...args);
79+
>super : Symbol(TBaseClass, Decl(accessorsOverrideProperty9.ts, 24, 31))
80+
>args : Symbol(args, Decl(accessorsOverrideProperty9.ts, 28, 23))
81+
}
82+
83+
public get members(): ReadonlyArray<ApiItem> {
84+
>members : Symbol(MixedClass.members, Decl(accessorsOverrideProperty9.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(accessorsOverrideProperty9.ts, 6, 91))
87+
88+
return [];
89+
}
90+
}
91+
92+
return MixedClass;
93+
>MixedClass : Symbol(MixedClass, Decl(accessorsOverrideProperty9.ts, 26, 65))
94+
}
95+
96+
// Subclass inheriting from mixin
97+
export class ApiEnum extends ApiItemContainerMixin(ApiItem) {
98+
>ApiEnum : Symbol(ApiEnum, Decl(accessorsOverrideProperty9.ts, 38, 1))
99+
>ApiItemContainerMixin : Symbol(ApiItemContainerMixin, Decl(accessorsOverrideProperty9.ts, 22, 1), Decl(accessorsOverrideProperty9.ts, 17, 1))
100+
>ApiItem : Symbol(ApiItem, Decl(accessorsOverrideProperty9.ts, 6, 91))
101+
102+
// This worked prior to TypeScript 4.0:
103+
public get members(): ReadonlyArray<ApiEnumMember> {
104+
>members : Symbol(ApiEnum.members, Decl(accessorsOverrideProperty9.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(accessorsOverrideProperty9.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/accessorsOverrideProperty9.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)