diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 047320c67a40e..532c25277cfc3 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -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); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fd50925352b1b..6c0781bccec96 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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(source) === getResolvedSymbol(target) || (target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) && getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source)) === getSymbolOfNode(target); diff --git a/tests/baselines/reference/controlFlowPrivateClassField.js b/tests/baselines/reference/controlFlowPrivateClassField.js new file mode 100644 index 0000000000000..0ec517e098d14 --- /dev/null +++ b/tests/baselines/reference/controlFlowPrivateClassField.js @@ -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; + } +} diff --git a/tests/baselines/reference/controlFlowPrivateClassField.symbols b/tests/baselines/reference/controlFlowPrivateClassField.symbols new file mode 100644 index 0000000000000..d2d9faf0bc4c9 --- /dev/null +++ b/tests/baselines/reference/controlFlowPrivateClassField.symbols @@ -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; + } +} diff --git a/tests/baselines/reference/controlFlowPrivateClassField.types b/tests/baselines/reference/controlFlowPrivateClassField.types new file mode 100644 index 0000000000000..cab3017d94f96 --- /dev/null +++ b/tests/baselines/reference/controlFlowPrivateClassField.types @@ -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 + } +} diff --git a/tests/cases/compiler/controlFlowPrivateClassField.ts b/tests/cases/compiler/controlFlowPrivateClassField.ts new file mode 100644 index 0000000000000..a9e8f5569b714 --- /dev/null +++ b/tests/cases/compiler/controlFlowPrivateClassField.ts @@ -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; + } +} \ No newline at end of file