Skip to content

Commit

Permalink
feat(47595): allow using private fields in type queries (microsoft#47696
Browse files Browse the repository at this point in the history
)
  • Loading branch information
a-tarasyuk authored Feb 15, 2022
1 parent 3b95404 commit 063eaa7
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 8 deletions.
16 changes: 8 additions & 8 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,7 @@ namespace ts {
initializeState("", content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS);
// Prime the scanner.
nextToken();
const entityName = parseEntityName(/*allowReservedWords*/ true);
const entityName = parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false);
const isInvalid = token() === SyntaxKind.EndOfFileToken && !parseDiagnostics.length;
clearState();
return isInvalid ? entityName : undefined;
Expand Down Expand Up @@ -2719,7 +2719,7 @@ namespace ts {
return createMissingList<T>();
}

function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName {
function parseEntityName(allowReservedWords: boolean, allowPrivateIdentifiers: boolean, diagnosticMessage?: DiagnosticMessage): EntityName {
const pos = getNodePos();
let entity: EntityName = allowReservedWords ? parseIdentifierName(diagnosticMessage) : parseIdentifier(diagnosticMessage);
let dotPos = getNodePos();
Expand All @@ -2733,7 +2733,7 @@ namespace ts {
entity = finishNode(
factory.createQualifiedName(
entity,
parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false) as Identifier
parseRightSideOfDot(allowReservedWords, allowPrivateIdentifiers) as Identifier
),
pos
);
Expand Down Expand Up @@ -2918,7 +2918,7 @@ namespace ts {
// TYPES

function parseEntityNameOfTypeReference() {
return parseEntityName(/*allowReservedWords*/ true, Diagnostics.Type_expected);
return parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false, Diagnostics.Type_expected);
}

function parseTypeArgumentsOfTypeReference() {
Expand Down Expand Up @@ -3078,7 +3078,7 @@ namespace ts {
function parseTypeQuery(): TypeQueryNode {
const pos = getNodePos();
parseExpected(SyntaxKind.TypeOfKeyword);
return finishNode(factory.createTypeQueryNode(parseEntityName(/*allowReservedWords*/ true)), pos);
return finishNode(factory.createTypeQueryNode(parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ true)), pos);
}

function parseTypeParameter(): TypeParameterDeclaration {
Expand Down Expand Up @@ -7351,7 +7351,7 @@ namespace ts {
function parseModuleReference() {
return isExternalModuleReference()
? parseExternalModuleReference()
: parseEntityName(/*allowReservedWords*/ false);
: parseEntityName(/*allowReservedWords*/ false, /*allowPrivateIdentifiers*/ false);
}

function parseExternalModuleReference() {
Expand Down Expand Up @@ -7659,7 +7659,7 @@ namespace ts {
const pos = getNodePos();
const hasBrace = parseOptional(SyntaxKind.OpenBraceToken);
const p2 = getNodePos();
let entityName: EntityName | JSDocMemberName = parseEntityName(/* allowReservedWords*/ false);
let entityName: EntityName | JSDocMemberName = parseEntityName(/* allowReservedWords*/ false, /*allowPrivateIdentifiers*/ false);
while (token() === SyntaxKind.PrivateIdentifier) {
reScanHashToken(); // rescan #id as # id
nextTokenJSDoc(); // then skip the #
Expand Down Expand Up @@ -8122,7 +8122,7 @@ namespace ts {
// parseEntityName logs an error for non-identifier, so create a MissingNode ourselves to avoid the error
const p2 = getNodePos();
let name: EntityName | JSDocMemberName | undefined = tokenIsIdentifierOrKeyword(token())
? parseEntityName(/*allowReservedWords*/ true)
? parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false)
: undefined;
if (name) {
while (token() === SyntaxKind.PrivateIdentifier) {
Expand Down
21 changes: 21 additions & 0 deletions tests/baselines/reference/privateNameInTypeQuery.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts(6,15): error TS2322: Type 'number' is not assignable to type 'string'.
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.


==== tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts (2 errors) ====
class C {
#a = 'a';

constructor() {
const a: typeof this.#a = ''; // Ok
const b: typeof this.#a = 1; // Error
~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
}
}

const c = new C();
const a: typeof c.#a = '';
~~
!!! error TS18013: Property '#a' is not accessible outside class 'C' because it has a private identifier.

25 changes: 25 additions & 0 deletions tests/baselines/reference/privateNameInTypeQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//// [privateNameInTypeQuery.ts]
class C {
#a = 'a';

constructor() {
const a: typeof this.#a = ''; // Ok
const b: typeof this.#a = 1; // Error
}
}

const c = new C();
const a: typeof c.#a = '';


//// [privateNameInTypeQuery.js]
"use strict";
class C {
#a = 'a';
constructor() {
const a = ''; // Ok
const b = 1; // Error
}
}
const c = new C();
const a = '';
28 changes: 28 additions & 0 deletions tests/baselines/reference/privateNameInTypeQuery.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts ===
class C {
>C : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))

#a = 'a';
>#a : Symbol(C.#a, Decl(privateNameInTypeQuery.ts, 0, 9))

constructor() {
const a: typeof this.#a = ''; // Ok
>a : Symbol(a, Decl(privateNameInTypeQuery.ts, 4, 13))
>this.#a : Symbol(C.#a, Decl(privateNameInTypeQuery.ts, 0, 9))
>this : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))

const b: typeof this.#a = 1; // Error
>b : Symbol(b, Decl(privateNameInTypeQuery.ts, 5, 13))
>this.#a : Symbol(C.#a, Decl(privateNameInTypeQuery.ts, 0, 9))
>this : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))
}
}

const c = new C();
>c : Symbol(c, Decl(privateNameInTypeQuery.ts, 9, 5))
>C : Symbol(C, Decl(privateNameInTypeQuery.ts, 0, 0))

const a: typeof c.#a = '';
>a : Symbol(a, Decl(privateNameInTypeQuery.ts, 10, 5))
>c : Symbol(c, Decl(privateNameInTypeQuery.ts, 9, 5))

34 changes: 34 additions & 0 deletions tests/baselines/reference/privateNameInTypeQuery.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInTypeQuery.ts ===
class C {
>C : C

#a = 'a';
>#a : string
>'a' : "a"

constructor() {
const a: typeof this.#a = ''; // Ok
>a : string
>this.#a : string
>this : this
>'' : ""

const b: typeof this.#a = 1; // Error
>b : string
>this.#a : string
>this : this
>1 : 1
}
}

const c = new C();
>c : C
>new C() : C
>C : typeof C

const a: typeof c.#a = '';
>a : any
>c.#a : any
>c : C
>'' : ""

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @strict: true
// @target: esnext

class C {
#a = 'a';

constructor() {
const a: typeof this.#a = ''; // Ok
const b: typeof this.#a = 1; // Error
}
}

const c = new C();
const a: typeof c.#a = '';

0 comments on commit 063eaa7

Please sign in to comment.