From 0dfbd9a07f6366d5a812f798139e13c550c43998 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:08:08 +0000 Subject: [PATCH 01/10] Initial plan From b4ad5e64031126f36522820bf3dc7d3ac8241310 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:28:50 +0000 Subject: [PATCH 02/10] Fix JS constructor this property declaration emit Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../transformers/declarations/transform.go | 5 +- ...ationEmitThisAssignmentWithBaseProperty.js | 62 ++++++++++++++ ...EmitThisAssignmentWithBaseProperty.symbols | 71 ++++++++++++++++ ...onEmitThisAssignmentWithBaseProperty.types | 85 +++++++++++++++++++ ...ationEmitThisAssignmentWithBaseProperty.ts | 39 +++++++++ 5 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js create mode 100644 testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols create mode 100644 testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types create mode 100644 testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index b6596e0d77..e77acb86d6 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -2049,7 +2049,6 @@ func (tx *DeclarationTransformer) visitThisPropertyAssignments(node *ast.Node) * if thisTarget != tx.enclosingDeclaration { return nil // stop searching within new `this` contexts } -caseBlock: switch ast.GetAssignmentDeclarationKind(node) { case ast.JSDeclarationKindThisProperty: name := ast.GetNameOfDeclaration(node) @@ -2067,8 +2066,8 @@ caseBlock: // there is a base type any assignments might be "from" tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information decls := tx.resolver.GetBaseDeclarationsForPropertyDeclaration(node) - if len(decls) > 0 { - break caseBlock // property lightly overrides a property in a base type - skip it + if len(decls) > 0 && !ast.IsConstructorDeclaration(thisContainer) { + break // property lightly overrides a property in a base type - skip it // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } } diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js new file mode 100644 index 0000000000..9d09402d1b --- /dev/null +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js @@ -0,0 +1,62 @@ +//// [tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts] //// + +//// [component.d.ts] +export class Component { + state: any; + constructor(props?: any); +} + +//// [main.js] +import { Component } from "./component"; + +export class C1 extends Component { + state = { count: 0 }; +} + +export class C2 extends Component { + constructor() { + super({}); + this.state = { count: 0 }; + } +} + +//// [mainTs.ts] +import { Component } from "./component"; + +export class C1 extends Component { + state = { count: 0 }; +} + +export class C2 extends Component { + constructor() { + super({}); + this.state = { count: 0 }; + } +} + + + + +//// [main.d.ts] +import { Component } from "./component"; +export declare class C1 extends Component { + state: { + count: number; + }; +} +export declare class C2 extends Component { + state: { + count: number; + }; + constructor(); +} +//// [mainTs.d.ts] +import { Component } from "./component"; +export declare class C1 extends Component { + state: { + count: number; + }; +} +export declare class C2 extends Component { + constructor(); +} diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols new file mode 100644 index 0000000000..53643aff22 --- /dev/null +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts] //// + +=== component.d.ts === +export class Component { +>Component : Symbol(Component, Decl(component.d.ts, 0, 0)) + + state: any; +>state : Symbol(Component.state, Decl(component.d.ts, 0, 24)) + + constructor(props?: any); +>props : Symbol(props, Decl(component.d.ts, 2, 16)) +} + +=== main.js === +import { Component } from "./component"; +>Component : Symbol(Component, Decl(main.js, 0, 8)) + +export class C1 extends Component { +>C1 : Symbol(C1, Decl(main.js, 0, 40)) +>Component : Symbol(Component, Decl(main.js, 0, 8)) + + state = { count: 0 }; +>state : Symbol(C1.state, Decl(main.js, 2, 35)) +>count : Symbol(count, Decl(main.js, 3, 13)) +} + +export class C2 extends Component { +>C2 : Symbol(C2, Decl(main.js, 4, 1)) +>Component : Symbol(Component, Decl(main.js, 0, 8)) + + constructor() { + super({}); +>super : Symbol(Component, Decl(component.d.ts, 0, 0)) + + this.state = { count: 0 }; +>this.state : Symbol(C2.state, Decl(main.js, 8, 18)) +>this : Symbol(C2, Decl(main.js, 4, 1)) +>state : Symbol(C2.state, Decl(main.js, 8, 18)) +>count : Symbol(count, Decl(main.js, 9, 22)) + } +} + +=== mainTs.ts === +import { Component } from "./component"; +>Component : Symbol(Component, Decl(mainTs.ts, 0, 8)) + +export class C1 extends Component { +>C1 : Symbol(C1, Decl(mainTs.ts, 0, 40)) +>Component : Symbol(Component, Decl(mainTs.ts, 0, 8)) + + state = { count: 0 }; +>state : Symbol(C1.state, Decl(mainTs.ts, 2, 35)) +>count : Symbol(count, Decl(mainTs.ts, 3, 13)) +} + +export class C2 extends Component { +>C2 : Symbol(C2, Decl(mainTs.ts, 4, 1)) +>Component : Symbol(Component, Decl(mainTs.ts, 0, 8)) + + constructor() { + super({}); +>super : Symbol(Component, Decl(component.d.ts, 0, 0)) + + this.state = { count: 0 }; +>this.state : Symbol(Component.state, Decl(component.d.ts, 0, 24)) +>this : Symbol(C2, Decl(mainTs.ts, 4, 1)) +>state : Symbol(Component.state, Decl(component.d.ts, 0, 24)) +>count : Symbol(count, Decl(mainTs.ts, 9, 22)) + } +} + diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types new file mode 100644 index 0000000000..2ee8c8a3d8 --- /dev/null +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types @@ -0,0 +1,85 @@ +//// [tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts] //// + +=== component.d.ts === +export class Component { +>Component : Component + + state: any; +>state : any + + constructor(props?: any); +>props : any +} + +=== main.js === +import { Component } from "./component"; +>Component : typeof Component + +export class C1 extends Component { +>C1 : C1 +>Component : Component + + state = { count: 0 }; +>state : { count: number; } +>{ count: 0 } : { count: number; } +>count : number +>0 : 0 +} + +export class C2 extends Component { +>C2 : C2 +>Component : Component + + constructor() { + super({}); +>super({}) : void +>super : typeof Component +>{} : {} + + this.state = { count: 0 }; +>this.state = { count: 0 } : { count: number; } +>this.state : any +>this : this +>state : any +>{ count: 0 } : { count: number; } +>count : number +>0 : 0 + } +} + +=== mainTs.ts === +import { Component } from "./component"; +>Component : typeof Component + +export class C1 extends Component { +>C1 : C1 +>Component : Component + + state = { count: 0 }; +>state : { count: number; } +>{ count: 0 } : { count: number; } +>count : number +>0 : 0 +} + +export class C2 extends Component { +>C2 : C2 +>Component : Component + + constructor() { + super({}); +>super({}) : void +>super : typeof Component +>{} : {} + + this.state = { count: 0 }; +>this.state = { count: 0 } : { count: number; } +>this.state : any +>this : this +>state : any +>{ count: 0 } : { count: number; } +>count : number +>0 : 0 + } +} + diff --git a/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts new file mode 100644 index 0000000000..5f7ed1b389 --- /dev/null +++ b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts @@ -0,0 +1,39 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @emitDeclarationOnly: true +// @target: esnext + +// @filename: component.d.ts +export class Component { + state: any; + constructor(props?: any); +} + +// @filename: main.js +import { Component } from "./component"; + +export class C1 extends Component { + state = { count: 0 }; +} + +export class C2 extends Component { + constructor() { + super({}); + this.state = { count: 0 }; + } +} + +// @filename: mainTs.ts +import { Component } from "./component"; + +export class C1 extends Component { + state = { count: 0 }; +} + +export class C2 extends Component { + constructor() { + super({}); + this.state = { count: 0 }; + } +} From 699cd170ee57a9109dab07a5ae88cad8351f2aa4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:33:16 +0000 Subject: [PATCH 03/10] Clarify base property skip comment Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/transformers/declarations/transform.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index e77acb86d6..95d75f926f 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -2067,7 +2067,7 @@ func (tx *DeclarationTransformer) visitThisPropertyAssignments(node *ast.Node) * tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information decls := tx.resolver.GetBaseDeclarationsForPropertyDeclaration(node) if len(decls) > 0 && !ast.IsConstructorDeclaration(thisContainer) { - break // property lightly overrides a property in a base type - skip it + break // non-constructor assignment to a base property - skip it // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } } From 51d48612ebd2f0da041cdc3ebcdd332180646a5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:34:09 +0000 Subject: [PATCH 04/10] Restore declaration emit switch label Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/transformers/declarations/transform.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index 95d75f926f..af311f1e51 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -2049,6 +2049,7 @@ func (tx *DeclarationTransformer) visitThisPropertyAssignments(node *ast.Node) * if thisTarget != tx.enclosingDeclaration { return nil // stop searching within new `this` contexts } +caseBlock: switch ast.GetAssignmentDeclarationKind(node) { case ast.JSDeclarationKindThisProperty: name := ast.GetNameOfDeclaration(node) @@ -2067,7 +2068,7 @@ func (tx *DeclarationTransformer) visitThisPropertyAssignments(node *ast.Node) * tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information decls := tx.resolver.GetBaseDeclarationsForPropertyDeclaration(node) if len(decls) > 0 && !ast.IsConstructorDeclaration(thisContainer) { - break // non-constructor assignment to a base property - skip it + break caseBlock // non-constructor assignment to a base property - skip it // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } } From 9855ab3b413deee4e3033c3efda8073c061168c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:34:57 +0000 Subject: [PATCH 05/10] Clarify non-constructor base property skip Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/transformers/declarations/transform.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index af311f1e51..89c095c4d8 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -2068,7 +2068,7 @@ caseBlock: tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information decls := tx.resolver.GetBaseDeclarationsForPropertyDeclaration(node) if len(decls) > 0 && !ast.IsConstructorDeclaration(thisContainer) { - break caseBlock // non-constructor assignment to a base property - skip it + break caseBlock // skip non-constructor assignments to base properties // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } } From 2341ccf69504c7a1edc62781a744fa2d58307643 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:23:02 +0000 Subject: [PATCH 06/10] Skip declaration emit for constructor writes to base accessors Even in a constructor, assigning to an inherited accessor via `this.prop = ...` is a setter call at runtime, so synthesizing a data property override can produce an invalid .d.ts. Continue skipping when any base declaration is an accessor. Also extend the regression test with non-constructor and accessor cases. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../transformers/declarations/transform.go | 4 +- ...ationEmitThisAssignmentWithBaseProperty.js | 28 ++++++++++- ...EmitThisAssignmentWithBaseProperty.symbols | 46 ++++++++++++++++- ...onEmitThisAssignmentWithBaseProperty.types | 50 ++++++++++++++++++- ...ationEmitThisAssignmentWithBaseProperty.ts | 20 +++++++- 5 files changed, 140 insertions(+), 8 deletions(-) diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index 89c095c4d8..d109a4c403 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -2067,8 +2067,8 @@ caseBlock: // there is a base type any assignments might be "from" tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information decls := tx.resolver.GetBaseDeclarationsForPropertyDeclaration(node) - if len(decls) > 0 && !ast.IsConstructorDeclaration(thisContainer) { - break caseBlock // skip non-constructor assignments to base properties + if len(decls) > 0 && (!ast.IsConstructorDeclaration(thisContainer) || core.Some(decls, ast.IsAccessor)) { + break caseBlock // skip non-constructor assignments to base properties, and any assignment to a base accessor // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } } diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js index 9d09402d1b..44dfe355ec 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js @@ -5,9 +5,14 @@ export class Component { state: any; constructor(props?: any); } + +export class WithAccessor { + get value(): number; + set value(v: number); +} //// [main.js] -import { Component } from "./component"; +import { Component, WithAccessor } from "./component"; export class C1 extends Component { state = { count: 0 }; @@ -19,6 +24,19 @@ export class C2 extends Component { this.state = { count: 0 }; } } + +export class C3 extends Component { + update() { + this.state = { count: 1 }; + } +} + +export class C4 extends WithAccessor { + constructor() { + super(); + this.value = 1; + } +} //// [mainTs.ts] import { Component } from "./component"; @@ -38,7 +56,7 @@ export class C2 extends Component { //// [main.d.ts] -import { Component } from "./component"; +import { Component, WithAccessor } from "./component"; export declare class C1 extends Component { state: { count: number; @@ -50,6 +68,12 @@ export declare class C2 extends Component { }; constructor(); } +export declare class C3 extends Component { + update(): void; +} +export declare class C4 extends WithAccessor { + constructor(); +} //// [mainTs.d.ts] import { Component } from "./component"; export declare class C1 extends Component { diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols index 53643aff22..c1769ab3da 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols @@ -11,12 +11,24 @@ export class Component { >props : Symbol(props, Decl(component.d.ts, 2, 16)) } +export class WithAccessor { +>WithAccessor : Symbol(WithAccessor, Decl(component.d.ts, 3, 1)) + + get value(): number; +>value : Symbol(WithAccessor.value, Decl(component.d.ts, 5, 27), Decl(component.d.ts, 6, 24)) + + set value(v: number); +>value : Symbol(WithAccessor.value, Decl(component.d.ts, 5, 27), Decl(component.d.ts, 6, 24)) +>v : Symbol(v, Decl(component.d.ts, 7, 14)) +} + === main.js === -import { Component } from "./component"; +import { Component, WithAccessor } from "./component"; >Component : Symbol(Component, Decl(main.js, 0, 8)) +>WithAccessor : Symbol(WithAccessor, Decl(main.js, 0, 19)) export class C1 extends Component { ->C1 : Symbol(C1, Decl(main.js, 0, 40)) +>C1 : Symbol(C1, Decl(main.js, 0, 54)) >Component : Symbol(Component, Decl(main.js, 0, 8)) state = { count: 0 }; @@ -40,6 +52,36 @@ export class C2 extends Component { } } +export class C3 extends Component { +>C3 : Symbol(C3, Decl(main.js, 11, 1)) +>Component : Symbol(Component, Decl(main.js, 0, 8)) + + update() { +>update : Symbol(C3.update, Decl(main.js, 13, 35)) + + this.state = { count: 1 }; +>this.state : Symbol(C3.state, Decl(main.js, 14, 14)) +>this : Symbol(C3, Decl(main.js, 11, 1)) +>state : Symbol(C3.state, Decl(main.js, 14, 14)) +>count : Symbol(count, Decl(main.js, 15, 22)) + } +} + +export class C4 extends WithAccessor { +>C4 : Symbol(C4, Decl(main.js, 17, 1)) +>WithAccessor : Symbol(WithAccessor, Decl(main.js, 0, 19)) + + constructor() { + super(); +>super : Symbol(WithAccessor, Decl(component.d.ts, 3, 1)) + + this.value = 1; +>this.value : Symbol(C4.value, Decl(main.js, 21, 16)) +>this : Symbol(C4, Decl(main.js, 17, 1)) +>value : Symbol(C4.value, Decl(main.js, 21, 16)) + } +} + === mainTs.ts === import { Component } from "./component"; >Component : Symbol(Component, Decl(mainTs.ts, 0, 8)) diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types index 2ee8c8a3d8..2037ba9192 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types @@ -11,9 +11,21 @@ export class Component { >props : any } +export class WithAccessor { +>WithAccessor : WithAccessor + + get value(): number; +>value : number + + set value(v: number); +>value : number +>v : number +} + === main.js === -import { Component } from "./component"; +import { Component, WithAccessor } from "./component"; >Component : typeof Component +>WithAccessor : typeof WithAccessor export class C1 extends Component { >C1 : C1 @@ -47,6 +59,42 @@ export class C2 extends Component { } } +export class C3 extends Component { +>C3 : C3 +>Component : Component + + update() { +>update : () => void + + this.state = { count: 1 }; +>this.state = { count: 1 } : { count: number; } +>this.state : any +>this : this +>state : any +>{ count: 1 } : { count: number; } +>count : number +>1 : 1 + } +} + +export class C4 extends WithAccessor { +>C4 : C4 +>WithAccessor : WithAccessor + + constructor() { + super(); +>super() : void +>super : typeof WithAccessor + + this.value = 1; +>this.value = 1 : 1 +>this.value : any +>this : this +>value : any +>1 : 1 + } +} + === mainTs.ts === import { Component } from "./component"; >Component : typeof Component diff --git a/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts index 5f7ed1b389..172d4a7132 100644 --- a/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts +++ b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts @@ -10,8 +10,13 @@ export class Component { constructor(props?: any); } +export class WithAccessor { + get value(): number; + set value(v: number); +} + // @filename: main.js -import { Component } from "./component"; +import { Component, WithAccessor } from "./component"; export class C1 extends Component { state = { count: 0 }; @@ -24,6 +29,19 @@ export class C2 extends Component { } } +export class C3 extends Component { + update() { + this.state = { count: 1 }; + } +} + +export class C4 extends WithAccessor { + constructor() { + super(); + this.value = 1; + } +} + // @filename: mainTs.ts import { Component } from "./component"; From 0abc622d919bc38bedb80cc08d6494b2eed386c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:29:26 +0000 Subject: [PATCH 07/10] Extract base-property skip condition into named variable Improves readability of the compound condition guarding declaration emit for this-property assignments. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/transformers/declarations/transform.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index d109a4c403..e9eea87a7d 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -2067,7 +2067,8 @@ caseBlock: // there is a base type any assignments might be "from" tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information decls := tx.resolver.GetBaseDeclarationsForPropertyDeclaration(node) - if len(decls) > 0 && (!ast.IsConstructorDeclaration(thisContainer) || core.Some(decls, ast.IsAccessor)) { + shouldSkipAssignment := len(decls) > 0 && (!ast.IsConstructorDeclaration(thisContainer) || core.Some(decls, ast.IsAccessor)) + if shouldSkipAssignment { break caseBlock // skip non-constructor assignments to base properties, and any assignment to a base accessor // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } From bb6a1468e4f0b07af05168a0eaa104d2d7a69cba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:12:51 +0000 Subject: [PATCH 08/10] Include implements clause props in base declaration lookup GetBaseDeclarationsForPropertyDeclaration only returned the first extends base type's declarations and ignored implements clauses. Collect property declarations from all base types and implements clause types so declaration emit of JS this-property assignments accounts for implemented members. Co-authored-by: weswigham <2932786+weswigham@users.noreply.github.com> --- internal/checker/checker.go | 18 +++++ internal/checker/emitresolver.go | 17 +++-- ...ationEmitThisAssignmentWithBaseProperty.js | 70 +++++++++++++++++++ ...EmitThisAssignmentWithBaseProperty.symbols | 12 ++++ ...onEmitThisAssignmentWithBaseProperty.types | 14 ++++ ...ationEmitThisAssignmentWithBaseProperty.ts | 7 ++ 6 files changed, 131 insertions(+), 7 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index a8223d2bac..b3830c402a 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -19102,6 +19102,24 @@ func (c *Checker) getBaseTypes(t *Type) []*Type { return data.resolvedBaseTypes } +func (c *Checker) getImplementsTypes(t *Type) []*Type { + var resolvedImplementsTypes []*Type + if t.symbol.Declarations != nil { + for _, declaration := range t.symbol.Declarations { + if !ast.IsClassLike(declaration) { + continue + } + for _, node := range ast.GetImplementsTypeNodes(declaration) { + implementsType := c.getTypeFromTypeNode(node) + if !c.isErrorType(implementsType) { + resolvedImplementsTypes = append(resolvedImplementsTypes, implementsType) + } + } + } + } + return resolvedImplementsTypes +} + func (c *Checker) getTupleBaseType(t *Type) *Type { typeParameters := t.AsTupleType().TypeParameters() elementInfos := t.AsTupleType().elementInfos diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 7bf3719e58..4e02ad7995 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -1286,13 +1286,16 @@ func (r *EmitResolver) GetBaseDeclarationsForPropertyDeclaration(node *ast.Node) if parentType == nil { return nil } - bases := r.checker.getBaseTypes(parentType) - for _, b := range bases { - baseProp := r.checker.getPropertyOfObjectType(b, s.Name) - if baseProp != nil { - return baseProp.Declarations - // TODO: return base declarations from all base types if any callers actually look at the list + var decls []*ast.Node + collect := func(bases []*Type) { + for _, b := range bases { + baseProp := r.checker.getPropertyOfObjectType(b, s.Name) + if baseProp != nil { + decls = append(decls, baseProp.Declarations...) + } } } - return nil + collect(r.checker.getBaseTypes(parentType)) + collect(r.checker.getImplementsTypes(parentType)) + return decls } diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js index 44dfe355ec..4776433308 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js @@ -37,6 +37,13 @@ export class C4 extends WithAccessor { this.value = 1; } } + +/** @implements {WithAccessor} */ +export class C5 { + constructor() { + this.value = 1; + } +} //// [mainTs.ts] import { Component } from "./component"; @@ -74,6 +81,10 @@ export declare class C3 extends Component { export declare class C4 extends WithAccessor { constructor(); } +/** @implements {WithAccessor} */ +export declare class C5 implements WithAccessor { + constructor(); +} //// [mainTs.d.ts] import { Component } from "./component"; export declare class C1 extends Component { @@ -84,3 +95,62 @@ export declare class C1 extends Component { export declare class C2 extends Component { constructor(); } + + +//// [DtsFileErrors] + + +main.d.ts(20,22): error TS2720: Class 'C5' incorrectly implements class 'WithAccessor'. Did you mean to extend 'WithAccessor' and inherit its members as a subclass? + Property 'value' is missing in type 'C5' but required in type 'WithAccessor'. + + +==== component.d.ts (0 errors) ==== + export class Component { + state: any; + constructor(props?: any); + } + + export class WithAccessor { + get value(): number; + set value(v: number); + } + +==== main.d.ts (1 errors) ==== + import { Component, WithAccessor } from "./component"; + export declare class C1 extends Component { + state: { + count: number; + }; + } + export declare class C2 extends Component { + state: { + count: number; + }; + constructor(); + } + export declare class C3 extends Component { + update(): void; + } + export declare class C4 extends WithAccessor { + constructor(); + } + /** @implements {WithAccessor} */ + export declare class C5 implements WithAccessor { + ~~ +!!! error TS2720: Class 'C5' incorrectly implements class 'WithAccessor'. Did you mean to extend 'WithAccessor' and inherit its members as a subclass? +!!! error TS2720: Property 'value' is missing in type 'C5' but required in type 'WithAccessor'. +!!! related TS2728 component.d.ts:7:9: 'value' is declared here. + constructor(); + } + +==== mainTs.d.ts (0 errors) ==== + import { Component } from "./component"; + export declare class C1 extends Component { + state: { + count: number; + }; + } + export declare class C2 extends Component { + constructor(); + } + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols index c1769ab3da..cb0bd99871 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols @@ -82,6 +82,18 @@ export class C4 extends WithAccessor { } } +/** @implements {WithAccessor} */ +export class C5 { +>C5 : Symbol(C5, Decl(main.js, 24, 1)) + + constructor() { + this.value = 1; +>this.value : Symbol(C5.value, Decl(main.js, 28, 19)) +>this : Symbol(C5, Decl(main.js, 24, 1)) +>value : Symbol(C5.value, Decl(main.js, 28, 19)) + } +} + === mainTs.ts === import { Component } from "./component"; >Component : Symbol(Component, Decl(mainTs.ts, 0, 8)) diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types index 2037ba9192..3e90643b44 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types @@ -95,6 +95,20 @@ export class C4 extends WithAccessor { } } +/** @implements {WithAccessor} */ +export class C5 { +>C5 : C5 + + constructor() { + this.value = 1; +>this.value = 1 : 1 +>this.value : any +>this : this +>value : any +>1 : 1 + } +} + === mainTs.ts === import { Component } from "./component"; >Component : typeof Component diff --git a/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts index 172d4a7132..9bd46156e5 100644 --- a/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts +++ b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts @@ -42,6 +42,13 @@ export class C4 extends WithAccessor { } } +/** @implements {WithAccessor} */ +export class C5 { + constructor() { + this.value = 1; + } +} + // @filename: mainTs.ts import { Component } from "./component"; From c05766209602820b2e835eb4405e71d4889a6f44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:32:39 +0000 Subject: [PATCH 09/10] Emit implements-clause this properties in declarations Skip decisions for JS constructor `this` assignments are based only on extends base declarations; implements-clause props are always emitted, as tsc does. Reverting the implements collection fixes the invalid C5 .d.ts. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/checker.go | 18 ------ internal/checker/emitresolver.go | 12 ++-- ...ationEmitThisAssignmentWithBaseProperty.js | 60 +------------------ 3 files changed, 5 insertions(+), 85 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index b3830c402a..a8223d2bac 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -19102,24 +19102,6 @@ func (c *Checker) getBaseTypes(t *Type) []*Type { return data.resolvedBaseTypes } -func (c *Checker) getImplementsTypes(t *Type) []*Type { - var resolvedImplementsTypes []*Type - if t.symbol.Declarations != nil { - for _, declaration := range t.symbol.Declarations { - if !ast.IsClassLike(declaration) { - continue - } - for _, node := range ast.GetImplementsTypeNodes(declaration) { - implementsType := c.getTypeFromTypeNode(node) - if !c.isErrorType(implementsType) { - resolvedImplementsTypes = append(resolvedImplementsTypes, implementsType) - } - } - } - } - return resolvedImplementsTypes -} - func (c *Checker) getTupleBaseType(t *Type) *Type { typeParameters := t.AsTupleType().TypeParameters() elementInfos := t.AsTupleType().elementInfos diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 4e02ad7995..4d099b893e 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -1287,15 +1287,11 @@ func (r *EmitResolver) GetBaseDeclarationsForPropertyDeclaration(node *ast.Node) return nil } var decls []*ast.Node - collect := func(bases []*Type) { - for _, b := range bases { - baseProp := r.checker.getPropertyOfObjectType(b, s.Name) - if baseProp != nil { - decls = append(decls, baseProp.Declarations...) - } + for _, b := range r.checker.getBaseTypes(parentType) { + baseProp := r.checker.getPropertyOfObjectType(b, s.Name) + if baseProp != nil { + decls = append(decls, baseProp.Declarations...) } } - collect(r.checker.getBaseTypes(parentType)) - collect(r.checker.getImplementsTypes(parentType)) return decls } diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js index 4776433308..bda18e263d 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js @@ -83,6 +83,7 @@ export declare class C4 extends WithAccessor { } /** @implements {WithAccessor} */ export declare class C5 implements WithAccessor { + value: number; constructor(); } //// [mainTs.d.ts] @@ -95,62 +96,3 @@ export declare class C1 extends Component { export declare class C2 extends Component { constructor(); } - - -//// [DtsFileErrors] - - -main.d.ts(20,22): error TS2720: Class 'C5' incorrectly implements class 'WithAccessor'. Did you mean to extend 'WithAccessor' and inherit its members as a subclass? - Property 'value' is missing in type 'C5' but required in type 'WithAccessor'. - - -==== component.d.ts (0 errors) ==== - export class Component { - state: any; - constructor(props?: any); - } - - export class WithAccessor { - get value(): number; - set value(v: number); - } - -==== main.d.ts (1 errors) ==== - import { Component, WithAccessor } from "./component"; - export declare class C1 extends Component { - state: { - count: number; - }; - } - export declare class C2 extends Component { - state: { - count: number; - }; - constructor(); - } - export declare class C3 extends Component { - update(): void; - } - export declare class C4 extends WithAccessor { - constructor(); - } - /** @implements {WithAccessor} */ - export declare class C5 implements WithAccessor { - ~~ -!!! error TS2720: Class 'C5' incorrectly implements class 'WithAccessor'. Did you mean to extend 'WithAccessor' and inherit its members as a subclass? -!!! error TS2720: Property 'value' is missing in type 'C5' but required in type 'WithAccessor'. -!!! related TS2728 component.d.ts:7:9: 'value' is declared here. - constructor(); - } - -==== mainTs.d.ts (0 errors) ==== - import { Component } from "./component"; - export declare class C1 extends Component { - state: { - count: number; - }; - } - export declare class C2 extends Component { - constructor(); - } - \ No newline at end of file From 2559767096d0f5959161cac83616394a5080bc2e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:02:53 +0000 Subject: [PATCH 10/10] Replicate strada serializePropertySymbol skip for this-prop expandos Co-authored-by: weswigham <2932786+weswigham@users.noreply.github.com> --- internal/checker/emitresolver.go | 38 ++++++++++++++----- internal/printer/emitresolver.go | 2 +- .../transformers/declarations/transform.go | 6 +-- ...ationEmitThisAssignmentWithBaseProperty.js | 18 ++++++++- ...EmitThisAssignmentWithBaseProperty.symbols | 33 +++++++++++++++- ...onEmitThisAssignmentWithBaseProperty.types | 34 ++++++++++++++++- ...ationEmitThisAssignmentWithBaseProperty.ts | 13 ++++++- 7 files changed, 123 insertions(+), 21 deletions(-) diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 4d099b893e..f5b68e1434 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -1270,9 +1270,20 @@ func (r *EmitResolver) TryJSTypeNodeToTypeNode(emitContext *printer.EmitContext, return requestNodeBuilder.TryJSTypeNodeToTypeNode(typeNode, enclosingDeclaration, flags, internalFlags, tracker) } -func (r *EmitResolver) GetBaseDeclarationsForPropertyDeclaration(node *ast.Node) []*ast.Node { +// IsThisPropertyAssignmentDeclarationRedundant reports whether a JS `this. = ...` expando +// assignment should be omitted from declaration emit because the member it would synthesize is +// already provided by an `extends` base type. This mirrors the skip condition in the checker's +// serializePropertySymbol: an inherited member is redundant when it is identical to the assigned +// one (same readonly-ness, optionality and type). Inherited accessors and methods are always +// treated as redundant here, since accessors merge oddly with value assignments (and run via the +// accessor at runtime), and `this`-expando props carry the ReplaceableByMethod contract, so a +// rebind such as `this.method = this.method.bind(this)` must not override the base method. +// +// Only `extends` base types are considered. Members coming from `implements` clauses are not +// inherited, so the class must redeclare them, and they are always emitted. +func (r *EmitResolver) IsThisPropertyAssignmentDeclarationRedundant(node *ast.Node) bool { if node == nil { - return nil + return false } r.checkerMu.Lock() @@ -1280,18 +1291,25 @@ func (r *EmitResolver) GetBaseDeclarationsForPropertyDeclaration(node *ast.Node) s := r.checker.getSymbolOfDeclaration(node) if s == nil || s.Parent == nil { - return nil + return false } parentType := r.checker.getDeclaredTypeOfSymbol(s.Parent) if parentType == nil { - return nil + return false } - var decls []*ast.Node - for _, b := range r.checker.getBaseTypes(parentType) { - baseProp := r.checker.getPropertyOfObjectType(b, s.Name) - if baseProp != nil { - decls = append(decls, baseProp.Declarations...) + for _, base := range r.checker.getBaseTypes(parentType) { + baseProp := r.checker.getPropertyOfType(base, s.Name) + if baseProp == nil { + continue + } + if baseProp.Flags&(ast.SymbolFlagsAccessor|ast.SymbolFlagsMethod|ast.SymbolFlagsFunction) != 0 { + return true + } + if r.checker.isReadonlySymbol(baseProp) == r.checker.isReadonlySymbol(s) && + (s.Flags&ast.SymbolFlagsOptional) == (baseProp.Flags&ast.SymbolFlagsOptional) && + r.checker.isTypeIdenticalTo(r.checker.getTypeOfSymbol(s), r.checker.getTypeOfSymbol(baseProp)) { + return true } } - return decls + return false } diff --git a/internal/printer/emitresolver.go b/internal/printer/emitresolver.go index 189fe88cce..8ac8a00d9a 100644 --- a/internal/printer/emitresolver.go +++ b/internal/printer/emitresolver.go @@ -111,7 +111,7 @@ type EmitResolver interface { GetEnumMemberValue(node *ast.Node) evaluator.Result IsLateBound(node *ast.Node) bool IsOptionalParameter(node *ast.Node) bool - GetBaseDeclarationsForPropertyDeclaration(node *ast.Node) []*ast.Node + IsThisPropertyAssignmentDeclarationRedundant(node *ast.Node) bool // isolatedDeclarations-specific declaration emit GetPropertiesOfContainerFunction(node *ast.Node) []*ast.Symbol diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index e9eea87a7d..3322b437cf 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -2066,10 +2066,8 @@ caseBlock: if thisTarget.ClassLikeData().HeritageClauses != nil && len(thisTarget.ClassLikeData().HeritageClauses.Nodes) > 0 && !isClassExtendingNull(thisTarget) { // there is a base type any assignments might be "from" tx.tracker.ReportInferenceFallback(thisTarget) // Add an isolated declarations error on this class - we can't know how to transform this prop into an assignment without referring to type information - decls := tx.resolver.GetBaseDeclarationsForPropertyDeclaration(node) - shouldSkipAssignment := len(decls) > 0 && (!ast.IsConstructorDeclaration(thisContainer) || core.Some(decls, ast.IsAccessor)) - if shouldSkipAssignment { - break caseBlock // skip non-constructor assignments to base properties, and any assignment to a base accessor + if tx.resolver.IsThisPropertyAssignmentDeclarationRedundant(node) { + break caseBlock // skip assignments whose member is already provided by an `extends` base type (an inherited accessor/method, or an identical inherited property) // TODO: If the property has an explicit `@type` annotation, we should probably emit it (maybe with an `override` modifier) instead of skipping it } } diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js index bda18e263d..874fb37548 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.js @@ -10,9 +10,13 @@ export class WithAccessor { get value(): number; set value(v: number); } + +export class WithMethod { + method(): void; +} //// [main.js] -import { Component, WithAccessor } from "./component"; +import { Component, WithAccessor, WithMethod } from "./component"; export class C1 extends Component { state = { count: 0 }; @@ -44,6 +48,13 @@ export class C5 { this.value = 1; } } + +export class C6 extends WithMethod { + constructor() { + super(); + this.method = this.method.bind(this); + } +} //// [mainTs.ts] import { Component } from "./component"; @@ -63,7 +74,7 @@ export class C2 extends Component { //// [main.d.ts] -import { Component, WithAccessor } from "./component"; +import { Component, WithAccessor, WithMethod } from "./component"; export declare class C1 extends Component { state: { count: number; @@ -86,6 +97,9 @@ export declare class C5 implements WithAccessor { value: number; constructor(); } +export declare class C6 extends WithMethod { + constructor(); +} //// [mainTs.d.ts] import { Component } from "./component"; export declare class C1 extends Component { diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols index cb0bd99871..f01d9a8d48 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.symbols @@ -22,13 +22,21 @@ export class WithAccessor { >v : Symbol(v, Decl(component.d.ts, 7, 14)) } +export class WithMethod { +>WithMethod : Symbol(WithMethod, Decl(component.d.ts, 8, 1)) + + method(): void; +>method : Symbol(WithMethod.method, Decl(component.d.ts, 10, 25)) +} + === main.js === -import { Component, WithAccessor } from "./component"; +import { Component, WithAccessor, WithMethod } from "./component"; >Component : Symbol(Component, Decl(main.js, 0, 8)) >WithAccessor : Symbol(WithAccessor, Decl(main.js, 0, 19)) +>WithMethod : Symbol(WithMethod, Decl(main.js, 0, 33)) export class C1 extends Component { ->C1 : Symbol(C1, Decl(main.js, 0, 54)) +>C1 : Symbol(C1, Decl(main.js, 0, 66)) >Component : Symbol(Component, Decl(main.js, 0, 8)) state = { count: 0 }; @@ -94,6 +102,27 @@ export class C5 { } } +export class C6 extends WithMethod { +>C6 : Symbol(C6, Decl(main.js, 31, 1)) +>WithMethod : Symbol(WithMethod, Decl(main.js, 0, 33)) + + constructor() { + super(); +>super : Symbol(WithMethod, Decl(component.d.ts, 8, 1)) + + this.method = this.method.bind(this); +>this.method : Symbol(C6.method, Decl(main.js, 35, 16)) +>this : Symbol(C6, Decl(main.js, 31, 1)) +>method : Symbol(C6.method, Decl(main.js, 35, 16)) +>this.method.bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>this.method : Symbol(C6.method, Decl(main.js, 35, 16)) +>this : Symbol(C6, Decl(main.js, 31, 1)) +>method : Symbol(C6.method, Decl(main.js, 35, 16)) +>bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>this : Symbol(C6, Decl(main.js, 31, 1)) + } +} + === mainTs.ts === import { Component } from "./component"; >Component : Symbol(Component, Decl(mainTs.ts, 0, 8)) diff --git a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types index 3e90643b44..68b7e075a2 100644 --- a/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types +++ b/testdata/baselines/reference/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.types @@ -22,10 +22,18 @@ export class WithAccessor { >v : number } +export class WithMethod { +>WithMethod : WithMethod + + method(): void; +>method : () => void +} + === main.js === -import { Component, WithAccessor } from "./component"; +import { Component, WithAccessor, WithMethod } from "./component"; >Component : typeof Component >WithAccessor : typeof WithAccessor +>WithMethod : typeof WithMethod export class C1 extends Component { >C1 : C1 @@ -109,6 +117,30 @@ export class C5 { } } +export class C6 extends WithMethod { +>C6 : C6 +>WithMethod : WithMethod + + constructor() { + super(); +>super() : void +>super : typeof WithMethod + + this.method = this.method.bind(this); +>this.method = this.method.bind(this) : () => void +>this.method : any +>this : this +>method : any +>this.method.bind(this) : () => void +>this.method.bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } +>this.method : () => void +>this : this +>method : () => void +>bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } +>this : this + } +} + === mainTs.ts === import { Component } from "./component"; >Component : typeof Component diff --git a/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts index 9bd46156e5..c5c42c0f70 100644 --- a/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts +++ b/testdata/tests/cases/compiler/jsDeclarationEmitThisAssignmentWithBaseProperty.ts @@ -15,8 +15,12 @@ export class WithAccessor { set value(v: number); } +export class WithMethod { + method(): void; +} + // @filename: main.js -import { Component, WithAccessor } from "./component"; +import { Component, WithAccessor, WithMethod } from "./component"; export class C1 extends Component { state = { count: 0 }; @@ -49,6 +53,13 @@ export class C5 { } } +export class C6 extends WithMethod { + constructor() { + super(); + this.method = this.method.bind(this); + } +} + // @filename: mainTs.ts import { Component } from "./component";