Allow private symbols to be control flow narrowed (#39978)

* Allow private symbols to be control flow narrowed

* Add test with narrowing of inferred control flow type for completeness

* Reformat long line
This commit is contained in:
Wesley Wigham
2020-09-05 02:48:45 -07:00
committed by GitHub
parent 8d7afc2b21
commit e5fd0dd1e3
6 changed files with 197 additions and 2 deletions

View File

@@ -831,6 +831,7 @@ namespace ts {
function isNarrowingExpression(expr: Expression): boolean {
switch (expr.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.PrivateIdentifier:
case SyntaxKind.ThisKeyword:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
@@ -850,7 +851,7 @@ namespace ts {
}
function isNarrowableReference(expr: Expression): boolean {
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.PrivateIdentifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) ||
isAssignmentExpression(expr) && isNarrowableReference(expr.left);

View File

@@ -8023,7 +8023,10 @@ namespace ts {
}
function getFlowTypeInConstructor(symbol: Symbol, constructor: ConstructorDeclaration) {
const reference = factory.createPropertyAccessExpression(factory.createThis(), unescapeLeadingUnderscores(symbol.escapedName));
const accessName = startsWith(symbol.escapedName as string, "__#")
? factory.createPrivateIdentifier((symbol.escapedName as string).split("@")[1])
: unescapeLeadingUnderscores(symbol.escapedName);
const reference = factory.createPropertyAccessExpression(factory.createThis(), accessName);
setParent(reference.expression, reference);
setParent(reference, constructor);
reference.flowNode = constructor.returnFlowNode;
@@ -20232,6 +20235,7 @@ namespace ts {
}
switch (source.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.PrivateIdentifier:
return target.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>source) === getResolvedSymbol(<Identifier>target) ||
(target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) &&
getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(<Identifier>source)) === getSymbolOfNode(target);

View File

@@ -0,0 +1,51 @@
//// [controlFlowPrivateClassField.ts]
class Example {
#test;
constructor(test: number) {
this.#test = test;
}
get test() {
return this.#test
}
}
class Example2 {
#test;
constructor(test: number | undefined) {
this.#test = test;
}
get test() {
if (this.#test) {
return this.#test
}
return 0;
}
}
//// [controlFlowPrivateClassField.js]
"use strict";
class Example {
constructor(test) {
this.#test = test;
}
#test;
get test() {
return this.#test;
}
}
class Example2 {
constructor(test) {
this.#test = test;
}
#test;
get test() {
if (this.#test) {
return this.#test;
}
return 0;
}
}

View File

@@ -0,0 +1,54 @@
=== tests/cases/compiler/controlFlowPrivateClassField.ts ===
class Example {
>Example : Symbol(Example, Decl(controlFlowPrivateClassField.ts, 0, 0))
#test;
>#test : Symbol(Example.#test, Decl(controlFlowPrivateClassField.ts, 0, 15))
constructor(test: number) {
>test : Symbol(test, Decl(controlFlowPrivateClassField.ts, 3, 16))
this.#test = test;
>this.#test : Symbol(Example.#test, Decl(controlFlowPrivateClassField.ts, 0, 15))
>this : Symbol(Example, Decl(controlFlowPrivateClassField.ts, 0, 0))
>test : Symbol(test, Decl(controlFlowPrivateClassField.ts, 3, 16))
}
get test() {
>test : Symbol(Example.test, Decl(controlFlowPrivateClassField.ts, 5, 5))
return this.#test
>this.#test : Symbol(Example.#test, Decl(controlFlowPrivateClassField.ts, 0, 15))
>this : Symbol(Example, Decl(controlFlowPrivateClassField.ts, 0, 0))
}
}
class Example2 {
>Example2 : Symbol(Example2, Decl(controlFlowPrivateClassField.ts, 10, 1))
#test;
>#test : Symbol(Example2.#test, Decl(controlFlowPrivateClassField.ts, 12, 16))
constructor(test: number | undefined) {
>test : Symbol(test, Decl(controlFlowPrivateClassField.ts, 15, 16))
this.#test = test;
>this.#test : Symbol(Example2.#test, Decl(controlFlowPrivateClassField.ts, 12, 16))
>this : Symbol(Example2, Decl(controlFlowPrivateClassField.ts, 10, 1))
>test : Symbol(test, Decl(controlFlowPrivateClassField.ts, 15, 16))
}
get test() {
>test : Symbol(Example2.test, Decl(controlFlowPrivateClassField.ts, 17, 5))
if (this.#test) {
>this.#test : Symbol(Example2.#test, Decl(controlFlowPrivateClassField.ts, 12, 16))
>this : Symbol(Example2, Decl(controlFlowPrivateClassField.ts, 10, 1))
return this.#test
>this.#test : Symbol(Example2.#test, Decl(controlFlowPrivateClassField.ts, 12, 16))
>this : Symbol(Example2, Decl(controlFlowPrivateClassField.ts, 10, 1))
}
return 0;
}
}

View File

@@ -0,0 +1,57 @@
=== tests/cases/compiler/controlFlowPrivateClassField.ts ===
class Example {
>Example : Example
#test;
>#test : number
constructor(test: number) {
>test : number
this.#test = test;
>this.#test = test : number
>this.#test : number
>this : this
>test : number
}
get test() {
>test : number
return this.#test
>this.#test : number
>this : this
}
}
class Example2 {
>Example2 : Example2
#test;
>#test : number | undefined
constructor(test: number | undefined) {
>test : number | undefined
this.#test = test;
>this.#test = test : number | undefined
>this.#test : number | undefined
>this : this
>test : number | undefined
}
get test() {
>test : number
if (this.#test) {
>this.#test : number | undefined
>this : this
return this.#test
>this.#test : number
>this : this
}
return 0;
>0 : 0
}
}

View File

@@ -0,0 +1,28 @@
// @strict: true
// @target: esnext
class Example {
#test;
constructor(test: number) {
this.#test = test;
}
get test() {
return this.#test
}
}
class Example2 {
#test;
constructor(test: number | undefined) {
this.#test = test;
}
get test() {
if (this.#test) {
return this.#test
}
return 0;
}
}