Skip to content

Commit d7576d5

Browse files
committed
handle private names in generics
Signed-off-by: Max Heiber <max.heiber@gmail.com>
1 parent c524116 commit d7576d5

10 files changed

+194
-9
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,6 @@ namespace ts {
732732
}
733733

734734
function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
735-
debugger
736735
const diagnostic = location
737736
? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
738737
: createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
@@ -2595,15 +2594,16 @@ namespace ts {
25952594
return getUnionType(arrayFrom(typeofEQFacts.keys(), getLiteralType));
25962595
}
25972596

2598-
// A reserved member name starts with two underscores, but the third character cannot be an underscore
2599-
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
2597+
// A reserved member name starts with two underscores, but the third character cannot be an underscore,
2598+
// @ or #. A third underscore indicates an escaped form of an identifer that started
26002599
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
2601-
// Symbol instance.
2600+
// Symbol instance and the # indicates that the name is a PrivateName.
26022601
function isReservedMemberName(name: __String) {
26032602
return (name as string).charCodeAt(0) === CharacterCodes._ &&
26042603
(name as string).charCodeAt(1) === CharacterCodes._ &&
26052604
(name as string).charCodeAt(2) !== CharacterCodes._ &&
2606-
(name as string).charCodeAt(2) !== CharacterCodes.at;
2605+
(name as string).charCodeAt(2) !== CharacterCodes.at &&
2606+
(name as string).charCodeAt(2) !== CharacterCodes.hash;
26072607
}
26082608

26092609
function getNamedMembers(members: SymbolTable): Symbol[] {
@@ -7277,7 +7277,7 @@ namespace ts {
72777277
* @param type a type to look up property from
72787278
* @param name a name of property to look up in a given type
72797279
*/
7280-
function getPropertyOfType(type: Type, name: __String, isPrivateName: boolean = false): Symbol | undefined {
7280+
function getPropertyOfType(type: Type, name: __String, isPrivateName = false): Symbol | undefined {
72817281
type = getApparentType(type);
72827282
if (type.flags & TypeFlags.Object) {
72837283
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts(5,9): error TS2339: Property '#foo' does not exist on type 'A'.
1+
tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts(5,9): error TS18007: Property '#foo' is only accessible within class 'A' because it has a private name.
22

33

44
==== tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts (1 errors) ====
@@ -8,5 +8,5 @@ tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOut
88

99
new A().#foo = 4; // Error
1010
~~~~
11-
!!! error TS2339: Property '#foo' does not exist on type 'A'.
11+
!!! error TS18007: Property '#foo' is only accessible within class 'A' because it has a private name.
1212

tests/baselines/reference/privateNameNotAccessibleOutsideDefiningClass.symbols

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ class A {
77
}
88

99
new A().#foo = 4; // Error
10+
>new A().#foo : Symbol(A[#foo], Decl(privateNameNotAccessibleOutsideDefiningClass.ts, 0, 9))
1011
>A : Symbol(A, Decl(privateNameNotAccessibleOutsideDefiningClass.ts, 0, 0))
1112

tests/baselines/reference/privateNameNotAccessibleOutsideDefiningClass.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class A {
99

1010
new A().#foo = 4; // Error
1111
>new A().#foo = 4 : 4
12-
>new A().#foo : any
12+
>new A().#foo : number
1313
>new A() : A
1414
>A : typeof A
1515
>4 : 4
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts(10,3): error TS18007: Property '#foo' is only accessible within class 'C<T>' because it has a private name.
2+
tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts(11,1): error TS2322: Type 'C<string>' is not assignable to type 'C<number>'.
3+
Type 'string' is not assignable to type 'number'.
4+
tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts(12,1): error TS2322: Type 'C<number>' is not assignable to type 'C<string>'.
5+
Type 'number' is not assignable to type 'string'.
6+
7+
8+
==== tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts (3 errors) ====
9+
class C<T> {
10+
#foo: T;
11+
bar(x: C<T>) { return x.#foo; } // OK
12+
baz(x: C<number>) { return x.#foo; } // OK
13+
quux(x: C<string>) { return x.#foo; } // OK
14+
}
15+
16+
declare let a: C<number>;
17+
declare let b: C<string>;
18+
a.#foo; // OK
19+
~~~~
20+
!!! error TS18007: Property '#foo' is only accessible within class 'C<T>' because it has a private name.
21+
a = b; // Error
22+
~
23+
!!! error TS2322: Type 'C<string>' is not assignable to type 'C<number>'.
24+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
25+
b = a; // Error
26+
~
27+
!!! error TS2322: Type 'C<number>' is not assignable to type 'C<string>'.
28+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
29+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [privateNamesInGenericClasses.ts]
2+
class C<T> {
3+
#foo: T;
4+
bar(x: C<T>) { return x.#foo; } // OK
5+
baz(x: C<number>) { return x.#foo; } // OK
6+
quux(x: C<string>) { return x.#foo; } // OK
7+
}
8+
9+
declare let a: C<number>;
10+
declare let b: C<string>;
11+
a.#foo; // OK
12+
a = b; // Error
13+
b = a; // Error
14+
15+
16+
//// [privateNamesInGenericClasses.js]
17+
var C = /** @class */ (function () {
18+
function C() {
19+
}
20+
C.prototype.bar = function (x) { return x.#foo; }; // OK
21+
C.prototype.baz = function (x) { return x.#foo; }; // OK
22+
C.prototype.quux = function (x) { return x.#foo; }; // OK
23+
return C;
24+
}());
25+
a.#foo; // OK
26+
a = b; // Error
27+
b = a; // Error
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts ===
2+
class C<T> {
3+
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
4+
>T : Symbol(T, Decl(privateNamesInGenericClasses.ts, 0, 8))
5+
6+
#foo: T;
7+
>#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
8+
>T : Symbol(T, Decl(privateNamesInGenericClasses.ts, 0, 8))
9+
10+
bar(x: C<T>) { return x.#foo; } // OK
11+
>bar : Symbol(C.bar, Decl(privateNamesInGenericClasses.ts, 1, 10))
12+
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 2, 6))
13+
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
14+
>T : Symbol(T, Decl(privateNamesInGenericClasses.ts, 0, 8))
15+
>x.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
16+
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 2, 6))
17+
18+
baz(x: C<number>) { return x.#foo; } // OK
19+
>baz : Symbol(C.baz, Decl(privateNamesInGenericClasses.ts, 2, 33))
20+
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 3, 6))
21+
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
22+
>x.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
23+
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 3, 6))
24+
25+
quux(x: C<string>) { return x.#foo; } // OK
26+
>quux : Symbol(C.quux, Decl(privateNamesInGenericClasses.ts, 3, 38))
27+
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 4, 7))
28+
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
29+
>x.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
30+
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 4, 7))
31+
}
32+
33+
declare let a: C<number>;
34+
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))
35+
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
36+
37+
declare let b: C<string>;
38+
>b : Symbol(b, Decl(privateNamesInGenericClasses.ts, 8, 11))
39+
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
40+
41+
a.#foo; // OK
42+
>a.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
43+
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))
44+
45+
a = b; // Error
46+
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))
47+
>b : Symbol(b, Decl(privateNamesInGenericClasses.ts, 8, 11))
48+
49+
b = a; // Error
50+
>b : Symbol(b, Decl(privateNamesInGenericClasses.ts, 8, 11))
51+
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))
52+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts ===
2+
class C<T> {
3+
>C : C<T>
4+
5+
#foo: T;
6+
>#foo : T
7+
8+
bar(x: C<T>) { return x.#foo; } // OK
9+
>bar : (x: C<T>) => T
10+
>x : C<T>
11+
>x.#foo : T
12+
>x : C<T>
13+
14+
baz(x: C<number>) { return x.#foo; } // OK
15+
>baz : (x: C<number>) => number
16+
>x : C<number>
17+
>x.#foo : number
18+
>x : C<number>
19+
20+
quux(x: C<string>) { return x.#foo; } // OK
21+
>quux : (x: C<string>) => string
22+
>x : C<string>
23+
>x.#foo : string
24+
>x : C<string>
25+
}
26+
27+
declare let a: C<number>;
28+
>a : C<number>
29+
30+
declare let b: C<string>;
31+
>b : C<string>
32+
33+
a.#foo; // OK
34+
>a.#foo : number
35+
>a : C<number>
36+
37+
a = b; // Error
38+
>a = b : C<string>
39+
>a : C<number>
40+
>b : C<string>
41+
42+
b = a; // Error
43+
>b = a : C<number>
44+
>b : C<string>
45+
>a : C<number>
46+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/conformance/classes/members/privateNames/privateNamesUnique.ts(9,7): error TS2322: Type 'B' is not assignable to type 'A'.
2+
Property '#foo' is missing in type 'B'.
3+
4+
5+
==== tests/cases/conformance/classes/members/privateNames/privateNamesUnique.ts (1 errors) ====
6+
class A {
7+
#foo: number;
8+
}
9+
10+
class B {
11+
#foo: number;
12+
}
13+
14+
const b: A = new B() // Error: Property #foo is missing
15+
~
16+
!!! error TS2322: Type 'B' is not assignable to type 'A'.
17+
!!! error TS2322: Property '#foo' is missing in type 'B'.
18+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class C<T> {
2+
#foo: T;
3+
bar(x: C<T>) { return x.#foo; } // OK
4+
baz(x: C<number>) { return x.#foo; } // OK
5+
quux(x: C<string>) { return x.#foo; } // OK
6+
}
7+
8+
declare let a: C<number>;
9+
declare let b: C<string>;
10+
a.#foo; // OK
11+
a = b; // Error
12+
b = a; // Error

0 commit comments

Comments
 (0)