Skip to content

Commit 7cd0e1a

Browse files
authored
Merge pull request microsoft#13990 from Microsoft/fixPrivateProtected
Properly handle private and protected properties in intersections
2 parents d24b689 + c870bef commit 7cd0e1a

File tree

11 files changed

+1204
-153
lines changed

11 files changed

+1204
-153
lines changed

src/compiler/checker.ts

Lines changed: 166 additions & 115 deletions
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,10 @@
17871787
"category": "Error",
17881788
"code": 2545
17891789
},
1790+
"Property '{0}' has conflicting declarations and is inaccessible in type '{1}'.": {
1791+
"category": "Error",
1792+
"code": 2546
1793+
},
17901794
"JSX element attributes type '{0}' may not be a union type.": {
17911795
"category": "Error",
17921796
"code": 2600

src/compiler/types.ts

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,37 +2602,34 @@
26022602

26032603
export const enum SymbolFlags {
26042604
None = 0,
2605-
FunctionScopedVariable = 0x00000001, // Variable (var) or parameter
2606-
BlockScopedVariable = 0x00000002, // A block-scoped variable (let or const)
2607-
Property = 0x00000004, // Property or enum member
2608-
EnumMember = 0x00000008, // Enum member
2609-
Function = 0x00000010, // Function
2610-
Class = 0x00000020, // Class
2611-
Interface = 0x00000040, // Interface
2612-
ConstEnum = 0x00000080, // Const enum
2613-
RegularEnum = 0x00000100, // Enum
2614-
ValueModule = 0x00000200, // Instantiated module
2615-
NamespaceModule = 0x00000400, // Uninstantiated module
2616-
TypeLiteral = 0x00000800, // Type Literal or mapped type
2617-
ObjectLiteral = 0x00001000, // Object Literal
2618-
Method = 0x00002000, // Method
2619-
Constructor = 0x00004000, // Constructor
2620-
GetAccessor = 0x00008000, // Get accessor
2621-
SetAccessor = 0x00010000, // Set accessor
2622-
Signature = 0x00020000, // Call, construct, or index signature
2623-
TypeParameter = 0x00040000, // Type parameter
2624-
TypeAlias = 0x00080000, // Type alias
2625-
ExportValue = 0x00100000, // Exported value marker (see comment in declareModuleMember in binder)
2626-
ExportType = 0x00200000, // Exported type marker (see comment in declareModuleMember in binder)
2627-
ExportNamespace = 0x00400000, // Exported namespace marker (see comment in declareModuleMember in binder)
2628-
Alias = 0x00800000, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
2629-
Instantiated = 0x01000000, // Instantiated symbol
2630-
Merged = 0x02000000, // Merged symbol (created during program binding)
2631-
Transient = 0x04000000, // Transient symbol (created during type check)
2632-
Prototype = 0x08000000, // Prototype property (no source representation)
2633-
SyntheticProperty = 0x10000000, // Property in union or intersection type
2634-
Optional = 0x20000000, // Optional property
2635-
ExportStar = 0x40000000, // Export * declaration
2605+
FunctionScopedVariable = 1 << 0, // Variable (var) or parameter
2606+
BlockScopedVariable = 1 << 1, // A block-scoped variable (let or const)
2607+
Property = 1 << 2, // Property or enum member
2608+
EnumMember = 1 << 3, // Enum member
2609+
Function = 1 << 4, // Function
2610+
Class = 1 << 5, // Class
2611+
Interface = 1 << 6, // Interface
2612+
ConstEnum = 1 << 7, // Const enum
2613+
RegularEnum = 1 << 8, // Enum
2614+
ValueModule = 1 << 9, // Instantiated module
2615+
NamespaceModule = 1 << 10, // Uninstantiated module
2616+
TypeLiteral = 1 << 11, // Type Literal or mapped type
2617+
ObjectLiteral = 1 << 12, // Object Literal
2618+
Method = 1 << 13, // Method
2619+
Constructor = 1 << 14, // Constructor
2620+
GetAccessor = 1 << 15, // Get accessor
2621+
SetAccessor = 1 << 16, // Set accessor
2622+
Signature = 1 << 17, // Call, construct, or index signature
2623+
TypeParameter = 1 << 18, // Type parameter
2624+
TypeAlias = 1 << 19, // Type alias
2625+
ExportValue = 1 << 20, // Exported value marker (see comment in declareModuleMember in binder)
2626+
ExportType = 1 << 21, // Exported type marker (see comment in declareModuleMember in binder)
2627+
ExportNamespace = 1 << 22, // Exported namespace marker (see comment in declareModuleMember in binder)
2628+
Alias = 1 << 23, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
2629+
Prototype = 1 << 24, // Prototype property (no source representation)
2630+
ExportStar = 1 << 25, // Export * declaration
2631+
Optional = 1 << 26, // Optional property
2632+
Transient = 1 << 27, // Transient symbol (created during type check)
26362633

26372634
Enum = RegularEnum | ConstEnum,
26382635
Variable = FunctionScopedVariable | BlockScopedVariable,
@@ -2692,11 +2689,9 @@
26922689
name: string; // Name of symbol
26932690
declarations?: Declaration[]; // Declarations associated with this symbol
26942691
valueDeclaration?: Declaration; // First value declaration of the symbol
2695-
26962692
members?: SymbolTable; // Class, interface or literal instance members
26972693
exports?: SymbolTable; // Module exports
26982694
globalExports?: SymbolTable; // Conditional global UMD exports
2699-
/* @internal */ isReadonly?: boolean; // readonly? (set only for intersections and unions)
27002695
/* @internal */ id?: number; // Unique id (used to look up SymbolLinks)
27012696
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
27022697
/* @internal */ parent?: Symbol; // Parent symbol
@@ -2721,8 +2716,6 @@
27212716
leftSpread?: Symbol; // Left source for synthetic spread property
27222717
rightSpread?: Symbol; // Right source for synthetic spread property
27232718
mappedTypeOrigin?: Symbol; // For a property on a mapped type, points back to the orignal 'T' from 'keyof T'.
2724-
hasNonUniformType?: boolean; // True if constituents have non-uniform types
2725-
isPartial?: boolean; // True if syntheric property of union type occurs in some but not all constituents
27262719
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
27272720
resolvedExports?: SymbolTable; // Resolved exports of module
27282721
exportsChecked?: boolean; // True if exports of external module have been checked
@@ -2732,7 +2725,22 @@
27322725
}
27332726

27342727
/* @internal */
2735-
export interface TransientSymbol extends Symbol, SymbolLinks { }
2728+
export const enum CheckFlags {
2729+
Instantiated = 1 << 0, // Instantiated symbol
2730+
SyntheticProperty = 1 << 1, // Property in union or intersection type
2731+
Readonly = 1 << 2, // Readonly transient symbol
2732+
Partial = 1 << 3, // Synthetic property present in some but not all constituents
2733+
HasNonUniformType = 1 << 4, // Synthetic property with non-uniform type in constituents
2734+
ContainsPublic = 1 << 5, // Synthetic property with public constituent(s)
2735+
ContainsProtected = 1 << 6, // Synthetic property with protected constituent(s)
2736+
ContainsPrivate = 1 << 7, // Synthetic property with private constituent(s)
2737+
ContainsStatic = 1 << 8, // Synthetic property with static constituent(s)
2738+
}
2739+
2740+
/* @internal */
2741+
export interface TransientSymbol extends Symbol, SymbolLinks {
2742+
checkFlags: CheckFlags;
2743+
}
27362744

27372745
export type SymbolTable = Map<Symbol>;
27382746

src/services/findAllReferences.ts

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

282282
// if this symbol is visible from its parent container, e.g. exported, then bail out
283283
// if symbol correspond to the union property - bail out
284-
if (symbol.parent || (symbol.flags & SymbolFlags.SyntheticProperty)) {
284+
if (symbol.parent || (symbol.flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty)) {
285285
return undefined;
286286
}
287287

src/services/symbolDisplay.ts

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

5353
if (flags & SymbolFlags.Property) {
54-
if (flags & SymbolFlags.SyntheticProperty) {
54+
if (flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty) {
5555
// If union property is result of union of non method (property/accessors/variables), it is labeled as property
5656
const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => {
5757
const rootSymbolFlags = rootSymbol.getFlags();
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
tests/cases/conformance/classes/mixinAccessModifiers.ts(39,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Private2'.
2+
tests/cases/conformance/classes/mixinAccessModifiers.ts(43,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Protected'.
3+
tests/cases/conformance/classes/mixinAccessModifiers.ts(47,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Public'.
4+
tests/cases/conformance/classes/mixinAccessModifiers.ts(51,4): error TS2445: Property 'p' is protected and only accessible within class 'Protected & Protected2' and its subclasses.
5+
tests/cases/conformance/classes/mixinAccessModifiers.ts(66,7): error TS2415: Class 'C1' incorrectly extends base class 'Private & Private2'.
6+
Type 'C1' is not assignable to type 'Private'.
7+
Property 'p' has conflicting declarations and is inaccessible in type 'C1'.
8+
tests/cases/conformance/classes/mixinAccessModifiers.ts(67,7): error TS2415: Class 'C2' incorrectly extends base class 'Private & Protected'.
9+
Type 'C2' is not assignable to type 'Private'.
10+
Property 'p' has conflicting declarations and is inaccessible in type 'C2'.
11+
tests/cases/conformance/classes/mixinAccessModifiers.ts(68,7): error TS2415: Class 'C3' incorrectly extends base class 'Private & Public'.
12+
Type 'C3' is not assignable to type 'Private'.
13+
Property 'p' has conflicting declarations and is inaccessible in type 'C3'.
14+
tests/cases/conformance/classes/mixinAccessModifiers.ts(85,6): error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
15+
tests/cases/conformance/classes/mixinAccessModifiers.ts(90,6): error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
16+
tests/cases/conformance/classes/mixinAccessModifiers.ts(98,6): error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
17+
tests/cases/conformance/classes/mixinAccessModifiers.ts(103,6): error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
18+
19+
20+
==== tests/cases/conformance/classes/mixinAccessModifiers.ts (11 errors) ====
21+
22+
type Constructable = new (...args: any[]) => object;
23+
24+
class Private {
25+
constructor (...args: any[]) {}
26+
private p: string;
27+
}
28+
29+
class Private2 {
30+
constructor (...args: any[]) {}
31+
private p: string;
32+
}
33+
34+
class Protected {
35+
constructor (...args: any[]) {}
36+
protected p: string;
37+
protected static s: string;
38+
}
39+
40+
class Protected2 {
41+
constructor (...args: any[]) {}
42+
protected p: string;
43+
protected static s: string;
44+
}
45+
46+
class Public {
47+
constructor (...args: any[]) {}
48+
public p: string;
49+
public static s: string;
50+
}
51+
52+
class Public2 {
53+
constructor (...args: any[]) {}
54+
public p: string;
55+
public static s: string;
56+
}
57+
58+
function f1(x: Private & Private2) {
59+
x.p; // Error, private constituent makes property inaccessible
60+
~
61+
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Private2'.
62+
}
63+
64+
function f2(x: Private & Protected) {
65+
x.p; // Error, private constituent makes property inaccessible
66+
~
67+
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Protected'.
68+
}
69+
70+
function f3(x: Private & Public) {
71+
x.p; // Error, private constituent makes property inaccessible
72+
~
73+
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Public'.
74+
}
75+
76+
function f4(x: Protected & Protected2) {
77+
x.p; // Error, protected when all constituents are protected
78+
~
79+
!!! error TS2445: Property 'p' is protected and only accessible within class 'Protected & Protected2' and its subclasses.
80+
}
81+
82+
function f5(x: Protected & Public) {
83+
x.p; // Ok, public if any constituent is public
84+
}
85+
86+
function f6(x: Public & Public2) {
87+
x.p; // Ok, public if any constituent is public
88+
}
89+
90+
declare function Mix<T, U>(c1: T, c2: U): T & U;
91+
92+
// Can't derive from type with inaccessible properties
93+
94+
class C1 extends Mix(Private, Private2) {}
95+
~~
96+
!!! error TS2415: Class 'C1' incorrectly extends base class 'Private & Private2'.
97+
!!! error TS2415: Type 'C1' is not assignable to type 'Private'.
98+
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C1'.
99+
class C2 extends Mix(Private, Protected) {}
100+
~~
101+
!!! error TS2415: Class 'C2' incorrectly extends base class 'Private & Protected'.
102+
!!! error TS2415: Type 'C2' is not assignable to type 'Private'.
103+
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C2'.
104+
class C3 extends Mix(Private, Public) {}
105+
~~
106+
!!! error TS2415: Class 'C3' incorrectly extends base class 'Private & Public'.
107+
!!! error TS2415: Type 'C3' is not assignable to type 'Private'.
108+
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C3'.
109+
110+
class C4 extends Mix(Protected, Protected2) {
111+
f(c4: C4, c5: C5, c6: C6) {
112+
c4.p;
113+
c5.p;
114+
c6.p;
115+
}
116+
static g() {
117+
C4.s;
118+
C5.s;
119+
C6.s
120+
}
121+
}
122+
123+
class C5 extends Mix(Protected, Public) {
124+
f(c4: C4, c5: C5, c6: C6) {
125+
c4.p; // Error, not in class deriving from Protected2
126+
~
127+
!!! error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
128+
c5.p;
129+
c6.p;
130+
}
131+
static g() {
132+
C4.s; // Error, not in class deriving from Protected2
133+
~
134+
!!! error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
135+
C5.s;
136+
C6.s
137+
}
138+
}
139+
140+
class C6 extends Mix(Public, Public2) {
141+
f(c4: C4, c5: C5, c6: C6) {
142+
c4.p; // Error, not in class deriving from Protected2
143+
~
144+
!!! error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
145+
c5.p;
146+
c6.p;
147+
}
148+
static g() {
149+
C4.s; // Error, not in class deriving from Protected2
150+
~
151+
!!! error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
152+
C5.s;
153+
C6.s
154+
}
155+
}
156+

0 commit comments

Comments
 (0)