Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions tsc/internal/transformers/estransforms/classfields.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ func (tx *classFieldsTransformer) visitForSubstitution(node *ast.Node) *ast.Node
return tx.visitIdentifier(node.AsIdentifier())
}
if node.Kind == ast.KindPropertyAccessExpression && ast.IsIdentifier(node.AsPropertyAccessExpression().Name()) {
return tx.visitPropertyAccessExpressionForSubstitution(node.AsPropertyAccessExpression())
return tx.visitPropertyAccessExpressionForSubstitution(node.AsPropertyAccessExpression(), tx.substitutionVisitor)
}
return tx.substitutionVisitor.VisitEachChild(node)
}
Expand Down Expand Up @@ -1097,16 +1097,16 @@ func (tx *classFieldsTransformer) visitPropertyAccessExpression(node *ast.Proper
// Private identifier names are still visited through VisitEachChild so they can be
// transformed by visitPrivateIdentifier.
if ast.IsIdentifier(node.Name()) {
return tx.visitPropertyAccessExpressionForSubstitution(node)
return tx.visitPropertyAccessExpressionForSubstitution(node, tx.Visitor())
}
return tx.Visitor().VisitEachChild(node.AsNode())
}

// visitPropertyAccessExpressionForSubstitution visits only the expression of a PropertyAccessExpression,
// leaving the name unchanged. This prevents the name from being treated as a standalone identifier
// reference and incorrectly substituted with a class alias.
func (tx *classFieldsTransformer) visitPropertyAccessExpressionForSubstitution(node *ast.PropertyAccessExpression) *ast.Node {
expression := tx.Visitor().VisitNode(node.Expression)
func (tx *classFieldsTransformer) visitPropertyAccessExpressionForSubstitution(node *ast.PropertyAccessExpression, visitor *ast.NodeVisitor) *ast.Node {
expression := visitor.VisitNode(node.Expression)
if expression != node.Expression {
return tx.Factory().UpdatePropertyAccessExpression(node, expression, node.QuestionDotToken, node.Name(), node.Flags)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//// [tests/cases/conformance/classes/classStaticBlock/classStaticBlock29.ts] ////

//// [classStaticBlock29.ts]
// https://github.com/microsoft/TypeScript/issues/64733

// Inside a downleveled `static {}` block, `this` refers to the class and is rewritten
// to the class alias, but `this` inside a regular function nested in the block refers
// to the function's own receiver and must be kept.
class Foo {
declare static count: number;
declare toObject: () => number;
declare getBar: () => () => number;
declare getIndexed: () => number;

bar() {
return 1;
}

static {
// Referencing the class by name makes the downleveled block use a class alias.
Foo.count = 0;
this.count++;
Foo.prototype.toObject = function (this: Foo) {
return this.bar();
};
Foo.prototype.getBar = function (this: Foo) {
return () => this.bar();
};
this.prototype.getIndexed = function (this: Foo) {
return this["bar"]();
};
}
}

new Foo().toObject();


//// [classStaticBlock29.js]
"use strict";
// https://github.com/microsoft/TypeScript/issues/64733
var _a;
// Inside a downleveled `static {}` block, `this` refers to the class and is rewritten
// to the class alias, but `this` inside a regular function nested in the block refers
// to the function's own receiver and must be kept.
class Foo {
bar() {
return 1;
}
}
_a = Foo;
(() => {
// Referencing the class by name makes the downleveled block use a class alias.
_a.count = 0;
_a.count++;
_a.prototype.toObject = function () {
return this.bar();
};
_a.prototype.getBar = function () {
return () => this.bar();
};
_a.prototype.getIndexed = function () {
return this["bar"]();
};
})();
new Foo().toObject();
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//// [tests/cases/conformance/classes/classStaticBlock/classStaticBlock29.ts] ////

=== classStaticBlock29.ts ===
// https://github.com/microsoft/TypeScript/issues/64733

// Inside a downleveled `static {}` block, `this` refers to the class and is rewritten
// to the class alias, but `this` inside a regular function nested in the block refers
// to the function's own receiver and must be kept.
class Foo {
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))

declare static count: number;
>count : Symbol(Foo.count, Decl(classStaticBlock29.ts, 5, 11))

declare toObject: () => number;
>toObject : Symbol(Foo.toObject, Decl(classStaticBlock29.ts, 6, 33))

declare getBar: () => () => number;
>getBar : Symbol(Foo.getBar, Decl(classStaticBlock29.ts, 7, 35))

declare getIndexed: () => number;
>getIndexed : Symbol(Foo.getIndexed, Decl(classStaticBlock29.ts, 8, 39))

bar() {
>bar : Symbol(Foo.bar, Decl(classStaticBlock29.ts, 9, 37))

return 1;
}

static {
// Referencing the class by name makes the downleveled block use a class alias.
Foo.count = 0;
>Foo.count : Symbol(Foo.count, Decl(classStaticBlock29.ts, 5, 11))
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))
>count : Symbol(Foo.count, Decl(classStaticBlock29.ts, 5, 11))

this.count++;
>this.count : Symbol(Foo.count, Decl(classStaticBlock29.ts, 5, 11))
>this : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))
>count : Symbol(Foo.count, Decl(classStaticBlock29.ts, 5, 11))

Foo.prototype.toObject = function (this: Foo) {
>Foo.prototype.toObject : Symbol(Foo.toObject, Decl(classStaticBlock29.ts, 6, 33))
>Foo.prototype : Symbol(Foo.prototype)
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))
>prototype : Symbol(Foo.prototype)
>toObject : Symbol(Foo.toObject, Decl(classStaticBlock29.ts, 6, 33))
>this : Symbol(this, Decl(classStaticBlock29.ts, 19, 43))
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))

return this.bar();
>this.bar : Symbol(Foo.bar, Decl(classStaticBlock29.ts, 9, 37))
>this : Symbol(this, Decl(classStaticBlock29.ts, 19, 43))
>bar : Symbol(Foo.bar, Decl(classStaticBlock29.ts, 9, 37))

};
Foo.prototype.getBar = function (this: Foo) {
>Foo.prototype.getBar : Symbol(Foo.getBar, Decl(classStaticBlock29.ts, 7, 35))
>Foo.prototype : Symbol(Foo.prototype)
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))
>prototype : Symbol(Foo.prototype)
>getBar : Symbol(Foo.getBar, Decl(classStaticBlock29.ts, 7, 35))
>this : Symbol(this, Decl(classStaticBlock29.ts, 22, 41))
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))

return () => this.bar();
>this.bar : Symbol(Foo.bar, Decl(classStaticBlock29.ts, 9, 37))
>this : Symbol(this, Decl(classStaticBlock29.ts, 22, 41))
>bar : Symbol(Foo.bar, Decl(classStaticBlock29.ts, 9, 37))

};
this.prototype.getIndexed = function (this: Foo) {
>this.prototype.getIndexed : Symbol(Foo.getIndexed, Decl(classStaticBlock29.ts, 8, 39))
>this.prototype : Symbol(Foo.prototype)
>this : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))
>prototype : Symbol(Foo.prototype)
>getIndexed : Symbol(Foo.getIndexed, Decl(classStaticBlock29.ts, 8, 39))
>this : Symbol(this, Decl(classStaticBlock29.ts, 25, 46))
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))

return this["bar"]();
>this : Symbol(this, Decl(classStaticBlock29.ts, 25, 46))
>"bar" : Symbol(Foo.bar, Decl(classStaticBlock29.ts, 9, 37))

};
}
}

new Foo().toObject();
>new Foo().toObject : Symbol(Foo.toObject, Decl(classStaticBlock29.ts, 6, 33))
>Foo : Symbol(Foo, Decl(classStaticBlock29.ts, 0, 0))
>toObject : Symbol(Foo.toObject, Decl(classStaticBlock29.ts, 6, 33))

Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//// [tests/cases/conformance/classes/classStaticBlock/classStaticBlock29.ts] ////

=== classStaticBlock29.ts ===
// https://github.com/microsoft/TypeScript/issues/64733

// Inside a downleveled `static {}` block, `this` refers to the class and is rewritten
// to the class alias, but `this` inside a regular function nested in the block refers
// to the function's own receiver and must be kept.
class Foo {
>Foo : Foo

declare static count: number;
>count : number

declare toObject: () => number;
>toObject : () => number

declare getBar: () => () => number;
>getBar : () => () => number

declare getIndexed: () => number;
>getIndexed : () => number

bar() {
>bar : () => number

return 1;
>1 : 1
}

static {
// Referencing the class by name makes the downleveled block use a class alias.
Foo.count = 0;
>Foo.count = 0 : 0
>Foo.count : number
>Foo : typeof Foo
>count : number
>0 : 0

this.count++;
>this.count++ : number
>this.count : number
>this : typeof Foo
>count : number

Foo.prototype.toObject = function (this: Foo) {
>Foo.prototype.toObject = function (this: Foo) { return this.bar(); } : (this: Foo) => number
>Foo.prototype.toObject : () => number
>Foo.prototype : Foo
>Foo : typeof Foo
>prototype : Foo
>toObject : () => number
>function (this: Foo) { return this.bar(); } : (this: Foo) => number
>this : Foo

return this.bar();
>this.bar() : number
>this.bar : () => number
>this : Foo
>bar : () => number

};
Foo.prototype.getBar = function (this: Foo) {
>Foo.prototype.getBar = function (this: Foo) { return () => this.bar(); } : (this: Foo) => () => number
>Foo.prototype.getBar : () => () => number
>Foo.prototype : Foo
>Foo : typeof Foo
>prototype : Foo
>getBar : () => () => number
>function (this: Foo) { return () => this.bar(); } : (this: Foo) => () => number
>this : Foo

return () => this.bar();
>() => this.bar() : () => number
>this.bar() : number
>this.bar : () => number
>this : Foo
>bar : () => number

};
this.prototype.getIndexed = function (this: Foo) {
>this.prototype.getIndexed = function (this: Foo) { return this["bar"](); } : (this: Foo) => number
>this.prototype.getIndexed : () => number
>this.prototype : Foo
>this : typeof Foo
>prototype : Foo
>getIndexed : () => number
>function (this: Foo) { return this["bar"](); } : (this: Foo) => number
>this : Foo

return this["bar"]();
>this["bar"]() : number
>this["bar"] : () => number
>this : Foo
>"bar" : "bar"

};
}
}

new Foo().toObject();
>new Foo().toObject() : number
>new Foo().toObject : () => number
>new Foo() : Foo
>Foo : typeof Foo
>toObject : () => number

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//// [tests/cases/conformance/classes/classStaticBlock/classStaticBlock29.ts] ////

//// [classStaticBlock29.ts]
// https://github.com/microsoft/TypeScript/issues/64733

// Inside a downleveled `static {}` block, `this` refers to the class and is rewritten
// to the class alias, but `this` inside a regular function nested in the block refers
// to the function's own receiver and must be kept.
class Foo {
declare static count: number;
declare toObject: () => number;
declare getBar: () => () => number;
declare getIndexed: () => number;

bar() {
return 1;
}

static {
// Referencing the class by name makes the downleveled block use a class alias.
Foo.count = 0;
this.count++;
Foo.prototype.toObject = function (this: Foo) {
return this.bar();
};
Foo.prototype.getBar = function (this: Foo) {
return () => this.bar();
};
this.prototype.getIndexed = function (this: Foo) {
return this["bar"]();
};
}
}

new Foo().toObject();


//// [classStaticBlock29.js]
"use strict";
// https://github.com/microsoft/TypeScript/issues/64733
// Inside a downleveled `static {}` block, `this` refers to the class and is rewritten
// to the class alias, but `this` inside a regular function nested in the block refers
// to the function's own receiver and must be kept.
class Foo {
bar() {
return 1;
}
static {
// Referencing the class by name makes the downleveled block use a class alias.
Foo.count = 0;
this.count++;
Foo.prototype.toObject = function () {
return this.bar();
};
Foo.prototype.getBar = function () {
return () => this.bar();
};
this.prototype.getIndexed = function () {
return this["bar"]();
};
}
}
new Foo().toObject();
Loading
Loading