Skip to content

Commit 99146e5

Browse files
committed
error message for private names in obj literals
Signed-off-by: Max Heiber <[email protected]>
1 parent 351c75c commit 99146e5

33 files changed

+688
-74
lines changed

src/compiler/binder.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,12 @@ namespace ts {
273273
}
274274
if (isPrivateName(name)) {
275275
// containingClass exists because private names only allowed inside classes
276-
const containingClassSymbol = getContainingClass(name.parent)!.symbol;
276+
const containingClass = getContainingClass(name.parent);
277+
if (!containingClass) {
278+
// we're in a case where there's a private name outside a class (invalid)
279+
return undefined;
280+
}
281+
const containingClassSymbol = containingClass.symbol;
277282
return getPropertyNameForPrivateNameDescription(containingClassSymbol, name.escapedText);
278283
}
279284
return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;

src/compiler/checker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29681,6 +29681,10 @@ namespace ts {
2968129681
return grammarErrorOnNode(prop.equalsToken!, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment);
2968229682
}
2968329683

29684+
if (name.kind === SyntaxKind.PrivateName) {
29685+
return grammarErrorOnNode(name, Diagnostics.Private_names_are_not_allowed_outside_class_bodies);
29686+
}
29687+
2968429688
// Modifiers are never allowed on properties except for 'async' on a method declaration
2968529689
if (prop.modifiers) {
2968629690
for (const mod of prop.modifiers!) { // TODO: GH#19955

src/compiler/diagnosticMessages.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4319,10 +4319,14 @@
43194319
"category": "Error",
43204320
"code": 18008
43214321
},
4322-
"Property '{0}' is missing in type '{1}'. While type '{1}' has a private member with the same spelling, its declaration and accessibility are distinct": {
4322+
"Property '{0}' is missing in type '{1}'. While type '{1}' has a private member with the same spelling, its declaration and accessibility are distinct.": {
43234323
"category": "Error",
43244324
"code": 18009
43254325
},
4326+
"Private names are not allowed outside class bodies.": {
4327+
"category": "Error",
4328+
"code": 18010
4329+
},
43264330

43274331
"File is a CommonJS module; it may be converted to an ES6 module.": {
43284332
"category": "Suggestion",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-1.ts(2,5): error TS18010: Private names are not allowed outside class bodies.
2+
3+
4+
==== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-1.ts (1 errors) ====
5+
const obj = {
6+
#foo: 1
7+
~~~~
8+
!!! error TS18010: Private names are not allowed outside class bodies.
9+
};
10+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [privateNameInObjectLiteral-1.ts]
2+
const obj = {
3+
#foo: 1
4+
};
5+
6+
7+
//// [privateNameInObjectLiteral-1.js]
8+
var obj = {
9+
#foo: 1
10+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-1.ts ===
2+
const obj = {
3+
>obj : Symbol(obj, Decl(privateNameInObjectLiteral-1.ts, 0, 5))
4+
5+
#foo: 1
6+
>#foo : Symbol(#foo, Decl(privateNameInObjectLiteral-1.ts, 0, 13))
7+
8+
};
9+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-1.ts ===
2+
const obj = {
3+
>obj : {}
4+
>{ #foo: 1} : {}
5+
6+
#foo: 1
7+
>#foo : number
8+
>1 : 1
9+
10+
};
11+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-2.ts(2,5): error TS18010: Private names are not allowed outside class bodies.
2+
3+
4+
==== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-2.ts (1 errors) ====
5+
const obj = {
6+
#foo() {
7+
~~~~
8+
!!! error TS18010: Private names are not allowed outside class bodies.
9+
10+
}
11+
};
12+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//// [privateNameInObjectLiteral-2.ts]
2+
const obj = {
3+
#foo() {
4+
5+
}
6+
};
7+
8+
9+
//// [privateNameInObjectLiteral-2.js]
10+
var obj = {
11+
#foo: function () {
12+
}
13+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-2.ts ===
2+
const obj = {
3+
>obj : Symbol(obj, Decl(privateNameInObjectLiteral-2.ts, 0, 5))
4+
5+
#foo() {
6+
>#foo : Symbol(#foo, Decl(privateNameInObjectLiteral-2.ts, 0, 13))
7+
8+
}
9+
};
10+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral-2.ts ===
2+
const obj = {
3+
>obj : {}
4+
>{ #foo() { }} : {}
5+
6+
#foo() {
7+
>#foo : () => void
8+
9+
}
10+
};
11+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral.ts(2,5): error TS18010: Private names are not allowed outside class bodies.
2+
3+
4+
==== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral.ts (1 errors) ====
5+
const obj = {
6+
#foo: "#foo",
7+
~~~~
8+
!!! error TS18010: Private names are not allowed outside class bodies.
9+
#bar: () => true,
10+
#baz() {
11+
return true;
12+
}
13+
};
14+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [privateNameInObjectLiteral.ts]
2+
const obj = {
3+
#foo: "#foo",
4+
#bar: () => true,
5+
#baz() {
6+
return true;
7+
}
8+
};
9+
10+
11+
//// [privateNameInObjectLiteral.js]
12+
var obj = {
13+
#foo: "#foo",
14+
#bar: function () { return true; },
15+
#baz: function () {
16+
return true;
17+
}
18+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral.ts ===
2+
const obj = {
3+
>obj : Symbol(obj, Decl(privateNameInObjectLiteral.ts, 0, 5))
4+
5+
#foo: "#foo",
6+
>#foo : Symbol(#foo, Decl(privateNameInObjectLiteral.ts, 0, 13))
7+
8+
#bar: () => true,
9+
>#bar : Symbol(#bar, Decl(privateNameInObjectLiteral.ts, 1, 17))
10+
11+
#baz() {
12+
>#baz : Symbol(#baz, Decl(privateNameInObjectLiteral.ts, 2, 21))
13+
14+
return true;
15+
}
16+
};
17+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInObjectLiteral.ts ===
2+
const obj = {
3+
>obj : {}
4+
>{ #foo: "#foo", #bar: () => true, #baz() { return true; }} : {}
5+
6+
#foo: "#foo",
7+
>#foo : string
8+
>"#foo" : "#foo"
9+
10+
#bar: () => true,
11+
>#bar : () => boolean
12+
>() => true : () => boolean
13+
>true : true
14+
15+
#baz() {
16+
>#baz : () => boolean
17+
18+
return true;
19+
>true : true
20+
}
21+
};
22+
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/privateNamesAndGenericClasses-2.ts(24,3): error TS2339: Property '#foo' does not exist on type 'C<number>'.
2+
tests/cases/conformance/classes/members/privateNames/privateNamesAndGenericClasses-2.ts(24,3): error TS18007: Property '#foo' is not accessible outside class 'C<T>' because it has a private name.
3+
tests/cases/conformance/classes/members/privateNames/privateNamesAndGenericClasses-2.ts(25,1): error TS2322: Type 'C<string>' is not assignable to type 'C<number>'.
4+
Type 'string' is not assignable to type 'number'.
5+
tests/cases/conformance/classes/members/privateNames/privateNamesAndGenericClasses-2.ts(26,1): error TS2322: Type 'C<number>' is not assignable to type 'C<string>'.
6+
Type 'number' is not assignable to type 'string'.
7+
8+
9+
==== tests/cases/conformance/classes/members/privateNames/privateNamesAndGenericClasses-2.ts (4 errors) ====
10+
class C<T> {
11+
#foo: T;
12+
#bar(): T {
13+
return this.#foo;
14+
}
15+
constructor(t: T) {
16+
this.#foo = t;
17+
t = this.#bar();
18+
}
19+
set baz(t: T) {
20+
this.#foo = t;
21+
22+
}
23+
get baz(): T {
24+
return this.#foo;
25+
}
26+
}
27+
28+
let a = new C(3);
29+
let b = new C("hello");
30+
31+
a.baz = 5 // OK
32+
const x: number = a.baz // OK
33+
a.#foo; // Error
34+
~~~~
35+
!!! error TS2339: Property '#foo' does not exist on type 'C<number>'.
36+
~~~~
37+
!!! error TS18007: Property '#foo' is not accessible outside class 'C<T>' because it has a private name.
38+
a = b; // Error
39+
~
40+
!!! error TS2322: Type 'C<string>' is not assignable to type 'C<number>'.
41+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
42+
b = a; // Error
43+
~
44+
!!! error TS2322: Type 'C<number>' is not assignable to type 'C<string>'.
45+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
46+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//// [privateNamesAndGenericClasses-2.ts]
2+
class C<T> {
3+
#foo: T;
4+
#bar(): T {
5+
return this.#foo;
6+
}
7+
constructor(t: T) {
8+
this.#foo = t;
9+
t = this.#bar();
10+
}
11+
set baz(t: T) {
12+
this.#foo = t;
13+
14+
}
15+
get baz(): T {
16+
return this.#foo;
17+
}
18+
}
19+
20+
let a = new C(3);
21+
let b = new C("hello");
22+
23+
a.baz = 5 // OK
24+
const x: number = a.baz // OK
25+
a.#foo; // Error
26+
a = b; // Error
27+
b = a; // Error
28+
29+
30+
//// [privateNamesAndGenericClasses-2.js]
31+
var _classPrivateFieldSet = function (receiver, privateMap, value) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to set private field on non-instance"); } privateMap.set(receiver, value); return value; };
32+
var _classPrivateFieldGet = function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); };
33+
var _foo;
34+
"use strict";
35+
class C {
36+
constructor(t) {
37+
_foo.set(this, void 0);
38+
_classPrivateFieldSet(this, _foo, t);
39+
t = this.#bar.call(this);
40+
}
41+
#bar() {
42+
return _classPrivateFieldGet(this, _foo);
43+
}
44+
set baz(t) {
45+
_classPrivateFieldSet(this, _foo, t);
46+
}
47+
get baz() {
48+
return _classPrivateFieldGet(this, _foo);
49+
}
50+
}
51+
_foo = new WeakMap();
52+
let a = new C(3);
53+
let b = new C("hello");
54+
a.baz = 5; // OK
55+
const x = a.baz; // OK
56+
a.#foo; // Error
57+
a = b; // Error
58+
b = a; // Error

0 commit comments

Comments
 (0)