Skip to content

Commit 1baa828

Browse files
committed
Propagate accessor flags in intersected properties
1 parent 3fe512a commit 1baa828

18 files changed

+828
-1
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14938,6 +14938,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1493814938
}
1493914939

1494014940
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined {
14941+
let propFlags = SymbolFlags.None;
1494114942
let singleProp: Symbol | undefined;
1494214943
let propSet: Map<SymbolId, Symbol> | undefined;
1494314944
let indexTypes: Type[] | undefined;
@@ -14964,6 +14965,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1496414965
}
1496514966
if (!singleProp) {
1496614967
singleProp = prop;
14968+
propFlags = (prop.flags & SymbolFlags.Accessor) || SymbolFlags.Property;
1496714969
}
1496814970
else if (prop !== singleProp) {
1496914971
const isInstantiation = (getTargetSymbol(prop) || prop) === (getTargetSymbol(singleProp) || singleProp);
@@ -14986,6 +14988,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1498614988
propSet.set(id, prop);
1498714989
}
1498814990
}
14991+
// classes created by mixins are represented as intersections and overriding a property in a derived class redefines it completely at runtime
14992+
// so a get accessor can't be merged with a set accessor in a base class, for that reason the accessor flags are only used when they are the same in all consistuents
14993+
if (propFlags & SymbolFlags.Accessor && (prop.flags & SymbolFlags.Accessor) !== (propFlags & SymbolFlags.Accessor)) {
14994+
propFlags = (propFlags & ~SymbolFlags.Accessor) | SymbolFlags.Property;
14995+
}
1498914996
}
1499014997
if (isUnion && isReadonlySymbol(prop)) {
1499114998
checkFlags |= CheckFlags.Readonly;
@@ -15004,6 +15011,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1500415011
else if (isUnion) {
1500515012
const indexInfo = !isLateBoundName(name) && getApplicableIndexInfoForName(type, name);
1500615013
if (indexInfo) {
15014+
propFlags = (propFlags & ~SymbolFlags.Accessor) | SymbolFlags.Property;
1500715015
checkFlags |= CheckFlags.WritePartial | (indexInfo.isReadonly ? CheckFlags.Readonly : 0);
1500815016
indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type);
1500915017
}
@@ -15082,7 +15090,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1508215090
propTypes.push(type);
1508315091
}
1508415092
addRange(propTypes, indexTypes);
15085-
const result = createSymbol(SymbolFlags.Property | (optionalFlag ?? 0), name, syntheticFlag | checkFlags);
15093+
const result = createSymbol(propFlags | (optionalFlag ?? 0), name, syntheticFlag | checkFlags);
1508615094
result.links.containingType = containingType;
1508715095
if (!hasNonUniformValueDeclaration && firstValueDeclaration) {
1508815096
result.valueDeclaration = firstValueDeclaration;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//// [tests/cases/conformance/classes/mixinAccessors1.ts] ////
2+
3+
//// [mixinAccessors1.ts]
4+
// https://github.com/microsoft/TypeScript/issues/58790
5+
6+
function mixin<T extends { new (...args: any[]): {} }>(superclass: T) {
7+
return class extends superclass {
8+
get validationTarget(): HTMLElement {
9+
return document.createElement("input");
10+
}
11+
};
12+
}
13+
14+
class BaseClass {
15+
get validationTarget(): HTMLElement {
16+
return document.createElement("div");
17+
}
18+
}
19+
20+
class MyClass extends mixin(BaseClass) {
21+
get validationTarget(): HTMLElement {
22+
return document.createElement("select");
23+
}
24+
}
25+
26+
//// [mixinAccessors1.js]
27+
"use strict";
28+
// https://github.com/microsoft/TypeScript/issues/58790
29+
function mixin(superclass) {
30+
return class extends superclass {
31+
get validationTarget() {
32+
return document.createElement("input");
33+
}
34+
};
35+
}
36+
class BaseClass {
37+
get validationTarget() {
38+
return document.createElement("div");
39+
}
40+
}
41+
class MyClass extends mixin(BaseClass) {
42+
get validationTarget() {
43+
return document.createElement("select");
44+
}
45+
}
46+
47+
48+
//// [mixinAccessors1.d.ts]
49+
declare function mixin<T extends {
50+
new (...args: any[]): {};
51+
}>(superclass: T): {
52+
new (...args: any[]): {
53+
get validationTarget(): HTMLElement;
54+
};
55+
} & T;
56+
declare class BaseClass {
57+
get validationTarget(): HTMLElement;
58+
}
59+
declare const MyClass_base: {
60+
new (...args: any[]): {
61+
get validationTarget(): HTMLElement;
62+
};
63+
} & typeof BaseClass;
64+
declare class MyClass extends MyClass_base {
65+
get validationTarget(): HTMLElement;
66+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//// [tests/cases/conformance/classes/mixinAccessors1.ts] ////
2+
3+
=== mixinAccessors1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/58790
5+
6+
function mixin<T extends { new (...args: any[]): {} }>(superclass: T) {
7+
>mixin : Symbol(mixin, Decl(mixinAccessors1.ts, 0, 0))
8+
>T : Symbol(T, Decl(mixinAccessors1.ts, 2, 15))
9+
>args : Symbol(args, Decl(mixinAccessors1.ts, 2, 32))
10+
>superclass : Symbol(superclass, Decl(mixinAccessors1.ts, 2, 55))
11+
>T : Symbol(T, Decl(mixinAccessors1.ts, 2, 15))
12+
13+
return class extends superclass {
14+
>superclass : Symbol(superclass, Decl(mixinAccessors1.ts, 2, 55))
15+
16+
get validationTarget(): HTMLElement {
17+
>validationTarget : Symbol((Anonymous class).validationTarget, Decl(mixinAccessors1.ts, 3, 35))
18+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
19+
20+
return document.createElement("input");
21+
>document.createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
22+
>document : Symbol(document, Decl(lib.dom.d.ts, --, --))
23+
>createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
24+
}
25+
};
26+
}
27+
28+
class BaseClass {
29+
>BaseClass : Symbol(BaseClass, Decl(mixinAccessors1.ts, 8, 1))
30+
31+
get validationTarget(): HTMLElement {
32+
>validationTarget : Symbol(BaseClass.validationTarget, Decl(mixinAccessors1.ts, 10, 17))
33+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
34+
35+
return document.createElement("div");
36+
>document.createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
37+
>document : Symbol(document, Decl(lib.dom.d.ts, --, --))
38+
>createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
39+
}
40+
}
41+
42+
class MyClass extends mixin(BaseClass) {
43+
>MyClass : Symbol(MyClass, Decl(mixinAccessors1.ts, 14, 1))
44+
>mixin : Symbol(mixin, Decl(mixinAccessors1.ts, 0, 0))
45+
>BaseClass : Symbol(BaseClass, Decl(mixinAccessors1.ts, 8, 1))
46+
47+
get validationTarget(): HTMLElement {
48+
>validationTarget : Symbol(MyClass.validationTarget, Decl(mixinAccessors1.ts, 16, 40))
49+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
50+
51+
return document.createElement("select");
52+
>document.createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
53+
>document : Symbol(document, Decl(lib.dom.d.ts, --, --))
54+
>createElement : Symbol(Document.createElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
55+
}
56+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//// [tests/cases/conformance/classes/mixinAccessors1.ts] ////
2+
3+
=== mixinAccessors1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/58790
5+
6+
function mixin<T extends { new (...args: any[]): {} }>(superclass: T) {
7+
>mixin : <T extends { new (...args: any[]): {}; }>(superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin<any>.(Anonymous class); } & T
8+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9+
>args : any[]
10+
> : ^^^^^
11+
>superclass : T
12+
> : ^
13+
14+
return class extends superclass {
15+
>class extends superclass { get validationTarget(): HTMLElement { return document.createElement("input"); } } : { new (...args: any[]): (Anonymous class); prototype: mixin<any>.(Anonymous class); } & T
16+
> : ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
>superclass : {}
18+
> : ^^
19+
20+
get validationTarget(): HTMLElement {
21+
>validationTarget : HTMLElement
22+
> : ^^^^^^^^^^^
23+
24+
return document.createElement("input");
25+
>document.createElement("input") : HTMLInputElement
26+
> : ^^^^^^^^^^^^^^^^
27+
>document.createElement : { <K extends keyof HTMLElementTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; <K extends keyof HTMLElementDeprecatedTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; }
28+
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^
29+
>document : Document
30+
> : ^^^^^^^^
31+
>createElement : { <K extends keyof HTMLElementTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; <K extends keyof HTMLElementDeprecatedTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; }
32+
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^
33+
>"input" : "input"
34+
> : ^^^^^^^
35+
}
36+
};
37+
}
38+
39+
class BaseClass {
40+
>BaseClass : BaseClass
41+
> : ^^^^^^^^^
42+
43+
get validationTarget(): HTMLElement {
44+
>validationTarget : HTMLElement
45+
> : ^^^^^^^^^^^
46+
47+
return document.createElement("div");
48+
>document.createElement("div") : HTMLDivElement
49+
> : ^^^^^^^^^^^^^^
50+
>document.createElement : { <K extends keyof HTMLElementTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; <K extends keyof HTMLElementDeprecatedTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; }
51+
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^
52+
>document : Document
53+
> : ^^^^^^^^
54+
>createElement : { <K extends keyof HTMLElementTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; <K extends keyof HTMLElementDeprecatedTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; }
55+
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^
56+
>"div" : "div"
57+
> : ^^^^^
58+
}
59+
}
60+
61+
class MyClass extends mixin(BaseClass) {
62+
>MyClass : MyClass
63+
> : ^^^^^^^
64+
>mixin(BaseClass) : mixin<typeof BaseClass>.(Anonymous class) & BaseClass
65+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
>mixin : <T extends { new (...args: any[]): {}; }>(superclass: T) => { new (...args: any[]): (Anonymous class); prototype: mixin<any>.(Anonymous class); } & T
67+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
68+
>BaseClass : typeof BaseClass
69+
> : ^^^^^^^^^^^^^^^^
70+
71+
get validationTarget(): HTMLElement {
72+
>validationTarget : HTMLElement
73+
> : ^^^^^^^^^^^
74+
75+
return document.createElement("select");
76+
>document.createElement("select") : HTMLSelectElement
77+
> : ^^^^^^^^^^^^^^^^^
78+
>document.createElement : { <K extends keyof HTMLElementTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; <K extends keyof HTMLElementDeprecatedTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; }
79+
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^
80+
>document : Document
81+
> : ^^^^^^^^
82+
>createElement : { <K extends keyof HTMLElementTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; <K extends keyof HTMLElementDeprecatedTagNameMap>(tagName: K, options?: ElementCreationOptions): HTMLElementDeprecatedTagNameMap[K]; (tagName: string, options?: ElementCreationOptions): HTMLElement; }
83+
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^^
84+
>"select" : "select"
85+
> : ^^^^^^^^
86+
}
87+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//// [tests/cases/conformance/classes/mixinAccessors2.ts] ////
2+
3+
//// [mixinAccessors2.ts]
4+
function mixin<T extends { new (...args: any[]): {} }>(superclass: T) {
5+
return class extends superclass {
6+
accessor name = "";
7+
};
8+
}
9+
10+
class BaseClass {
11+
accessor name = "";
12+
}
13+
14+
class MyClass extends mixin(BaseClass) {
15+
accessor name = "";
16+
}
17+
18+
19+
//// [mixinAccessors2.js]
20+
"use strict";
21+
function mixin(superclass) {
22+
return class extends superclass {
23+
accessor name = "";
24+
};
25+
}
26+
class BaseClass {
27+
accessor name = "";
28+
}
29+
class MyClass extends mixin(BaseClass) {
30+
accessor name = "";
31+
}
32+
33+
34+
//// [mixinAccessors2.d.ts]
35+
declare function mixin<T extends {
36+
new (...args: any[]): {};
37+
}>(superclass: T): {
38+
new (...args: any[]): {
39+
get name(): string;
40+
set name(arg: string);
41+
};
42+
} & T;
43+
declare class BaseClass {
44+
accessor name: string;
45+
}
46+
declare const MyClass_base: {
47+
new (...args: any[]): {
48+
get name(): string;
49+
set name(arg: string);
50+
};
51+
} & typeof BaseClass;
52+
declare class MyClass extends MyClass_base {
53+
accessor name: string;
54+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [tests/cases/conformance/classes/mixinAccessors2.ts] ////
2+
3+
=== mixinAccessors2.ts ===
4+
function mixin<T extends { new (...args: any[]): {} }>(superclass: T) {
5+
>mixin : Symbol(mixin, Decl(mixinAccessors2.ts, 0, 0))
6+
>T : Symbol(T, Decl(mixinAccessors2.ts, 0, 15))
7+
>args : Symbol(args, Decl(mixinAccessors2.ts, 0, 32))
8+
>superclass : Symbol(superclass, Decl(mixinAccessors2.ts, 0, 55))
9+
>T : Symbol(T, Decl(mixinAccessors2.ts, 0, 15))
10+
11+
return class extends superclass {
12+
>superclass : Symbol(superclass, Decl(mixinAccessors2.ts, 0, 55))
13+
14+
accessor name = "";
15+
>name : Symbol((Anonymous class).name, Decl(mixinAccessors2.ts, 1, 35))
16+
17+
};
18+
}
19+
20+
class BaseClass {
21+
>BaseClass : Symbol(BaseClass, Decl(mixinAccessors2.ts, 4, 1))
22+
23+
accessor name = "";
24+
>name : Symbol(BaseClass.name, Decl(mixinAccessors2.ts, 6, 17))
25+
}
26+
27+
class MyClass extends mixin(BaseClass) {
28+
>MyClass : Symbol(MyClass, Decl(mixinAccessors2.ts, 8, 1))
29+
>mixin : Symbol(mixin, Decl(mixinAccessors2.ts, 0, 0))
30+
>BaseClass : Symbol(BaseClass, Decl(mixinAccessors2.ts, 4, 1))
31+
32+
accessor name = "";
33+
>name : Symbol(MyClass.name, Decl(mixinAccessors2.ts, 10, 40))
34+
}
35+

0 commit comments

Comments
 (0)