Skip to content

Commit 063eaa7

Browse files
authored
feat(47595): allow using private fields in type queries (#47696)
1 parent 3b95404 commit 063eaa7

File tree

6 files changed

+130
-8
lines changed

6 files changed

+130
-8
lines changed

src/compiler/parser.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ namespace ts {
875875
initializeState("", content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS);
876876
// Prime the scanner.
877877
nextToken();
878-
const entityName = parseEntityName(/*allowReservedWords*/ true);
878+
const entityName = parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false);
879879
const isInvalid = token() === SyntaxKind.EndOfFileToken && !parseDiagnostics.length;
880880
clearState();
881881
return isInvalid ? entityName : undefined;
@@ -2719,7 +2719,7 @@ namespace ts {
27192719
return createMissingList<T>();
27202720
}
27212721

2722-
function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName {
2722+
function parseEntityName(allowReservedWords: boolean, allowPrivateIdentifiers: boolean, diagnosticMessage?: DiagnosticMessage): EntityName {
27232723
const pos = getNodePos();
27242724
let entity: EntityName = allowReservedWords ? parseIdentifierName(diagnosticMessage) : parseIdentifier(diagnosticMessage);
27252725
let dotPos = getNodePos();
@@ -2733,7 +2733,7 @@ namespace ts {
27332733
entity = finishNode(
27342734
factory.createQualifiedName(
27352735
entity,
2736-
parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false) as Identifier
2736+
parseRightSideOfDot(allowReservedWords, allowPrivateIdentifiers) as Identifier
27372737
),
27382738
pos
27392739
);
@@ -2918,7 +2918,7 @@ namespace ts {
29182918
// TYPES
29192919

29202920
function parseEntityNameOfTypeReference() {
2921-
return parseEntityName(/*allowReservedWords*/ true, Diagnostics.Type_expected);
2921+
return parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false, Diagnostics.Type_expected);
29222922
}
29232923

29242924
function parseTypeArgumentsOfTypeReference() {
@@ -3078,7 +3078,7 @@ namespace ts {
30783078
function parseTypeQuery(): TypeQueryNode {
30793079
const pos = getNodePos();
30803080
parseExpected(SyntaxKind.TypeOfKeyword);
3081-
return finishNode(factory.createTypeQueryNode(parseEntityName(/*allowReservedWords*/ true)), pos);
3081+
return finishNode(factory.createTypeQueryNode(parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ true)), pos);
30823082
}
30833083

30843084
function parseTypeParameter(): TypeParameterDeclaration {
@@ -7351,7 +7351,7 @@ namespace ts {
73517351
function parseModuleReference() {
73527352
return isExternalModuleReference()
73537353
? parseExternalModuleReference()
7354-
: parseEntityName(/*allowReservedWords*/ false);
7354+
: parseEntityName(/*allowReservedWords*/ false, /*allowPrivateIdentifiers*/ false);
73557355
}
73567356

73577357
function parseExternalModuleReference() {
@@ -7659,7 +7659,7 @@ namespace ts {
76597659
const pos = getNodePos();
76607660
const hasBrace = parseOptional(SyntaxKind.OpenBraceToken);
76617661
const p2 = getNodePos();
7662-
let entityName: EntityName | JSDocMemberName = parseEntityName(/* allowReservedWords*/ false);
7662+
let entityName: EntityName | JSDocMemberName = parseEntityName(/* allowReservedWords*/ false, /*allowPrivateIdentifiers*/ false);
76637663
while (token() === SyntaxKind.PrivateIdentifier) {
76647664
reScanHashToken(); // rescan #id as # id
76657665
nextTokenJSDoc(); // then skip the #
@@ -8122,7 +8122,7 @@ namespace ts {
81228122
// parseEntityName logs an error for non-identifier, so create a MissingNode ourselves to avoid the error
81238123
const p2 = getNodePos();
81248124
let name: EntityName | JSDocMemberName | undefined = tokenIsIdentifierOrKeyword(token())
8125-
? parseEntityName(/*allowReservedWords*/ true)
8125+
? parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false)
81268126
: undefined;
81278127
if (name) {
81288128
while (token() === SyntaxKind.PrivateIdentifier) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts(6,15): error TS2322: Type 'number' is not assignable to type 'string'.
2+
tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts(11,19): error TS18013: Property '#a' is not accessible outside class 'C' because it has a private identifier.
3+
4+
5+
==== tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts (2 errors) ====
6+
class C {
7+
#a = 'a';
8+
9+
constructor() {
10+
const a: typeof this.#a = ''; // Ok
11+
const b: typeof this.#a = 1; // Error
12+
~
13+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
14+
}
15+
}
16+
17+
const c = new C();
18+
const a: typeof c.#a = '';
19+
~~
20+
!!! error TS18013: Property '#a' is not accessible outside class 'C' because it has a private identifier.
21+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [privateNameInTypeQuery.ts]
2+
class C {
3+
#a = 'a';
4+
5+
constructor() {
6+
const a: typeof this.#a = ''; // Ok
7+
const b: typeof this.#a = 1; // Error
8+
}
9+
}
10+
11+
const c = new C();
12+
const a: typeof c.#a = '';
13+
14+
15+
//// [privateNameInTypeQuery.js]
16+
"use strict";
17+
class C {
18+
#a = 'a';
19+
constructor() {
20+
const a = ''; // Ok
21+
const b = 1; // Error
22+
}
23+
}
24+
const c = new C();
25+
const a = '';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts ===
2+
class C {
3+
>C : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))
4+
5+
#a = 'a';
6+
>#a : Symbol(C.#a, Decl(privateNameInTypeQuery.ts, 0, 9))
7+
8+
constructor() {
9+
const a: typeof this.#a = ''; // Ok
10+
>a : Symbol(a, Decl(privateNameInTypeQuery.ts, 4, 13))
11+
>this.#a : Symbol(C.#a, Decl(privateNameInTypeQuery.ts, 0, 9))
12+
>this : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))
13+
14+
const b: typeof this.#a = 1; // Error
15+
>b : Symbol(b, Decl(privateNameInTypeQuery.ts, 5, 13))
16+
>this.#a : Symbol(C.#a, Decl(privateNameInTypeQuery.ts, 0, 9))
17+
>this : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))
18+
}
19+
}
20+
21+
const c = new C();
22+
>c : Symbol(c, Decl(privateNameInTypeQuery.ts, 9, 5))
23+
>C : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))
24+
25+
const a: typeof c.#a = '';
26+
>a : Symbol(a, Decl(privateNameInTypeQuery.ts, 10, 5))
27+
>c : Symbol(c, Decl(privateNameInTypeQuery.ts, 9, 5))
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts ===
2+
class C {
3+
>C : C
4+
5+
#a = 'a';
6+
>#a : string
7+
>'a' : "a"
8+
9+
constructor() {
10+
const a: typeof this.#a = ''; // Ok
11+
>a : string
12+
>this.#a : string
13+
>this : this
14+
>'' : ""
15+
16+
const b: typeof this.#a = 1; // Error
17+
>b : string
18+
>this.#a : string
19+
>this : this
20+
>1 : 1
21+
}
22+
}
23+
24+
const c = new C();
25+
>c : C
26+
>new C() : C
27+
>C : typeof C
28+
29+
const a: typeof c.#a = '';
30+
>a : any
31+
>c.#a : any
32+
>c : C
33+
>'' : ""
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @strict: true
2+
// @target: esnext
3+
4+
class C {
5+
#a = 'a';
6+
7+
constructor() {
8+
const a: typeof this.#a = ''; // Ok
9+
const b: typeof this.#a = 1; // Error
10+
}
11+
}
12+
13+
const c = new C();
14+
const a: typeof c.#a = '';

0 commit comments

Comments
 (0)