From 7fd4827b14175dc698cb72766584db955d9aa236 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 21:17:54 +0000 Subject: [PATCH 1/3] Initial plan From 511d3a96d386a889daea3fb7282e07a8bc69a503 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 21:51:18 +0000 Subject: [PATCH 2/3] Add PropertyAssignment node reuse logic from TypeScript PR #60576 Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- internal/ast/utilities.go | 4 +-- internal/checker/nodebuilderimpl.go | 44 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 4968528f9d..9b1cdd2edb 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -790,7 +790,7 @@ func IsJSDocKind(kind Kind) bool { return KindFirstJSDocNode <= kind && kind <= KindLastJSDocNode } -func isJSDocTypeAssertion(_ *Node) bool { +func IsJSDocTypeAssertion(_ *Node) bool { return false // !!! } @@ -817,7 +817,7 @@ const ( func IsOuterExpression(node *Expression, kinds OuterExpressionKinds) bool { switch node.Kind { case KindParenthesizedExpression: - return kinds&OEKParentheses != 0 && !(kinds&OEKExcludeJSDocTypeAssertion != 0 && isJSDocTypeAssertion(node)) + return kinds&OEKParentheses != 0 && !(kinds&OEKExcludeJSDocTypeAssertion != 0 && IsJSDocTypeAssertion(node)) case KindTypeAssertionExpression, KindAsExpression: return kinds&OEKTypeAssertions != 0 case KindSatisfiesExpression: diff --git a/internal/checker/nodebuilderimpl.go b/internal/checker/nodebuilderimpl.go index afdc9494d7..240e35af16 100644 --- a/internal/checker/nodebuilderimpl.go +++ b/internal/checker/nodebuilderimpl.go @@ -1637,6 +1637,13 @@ func (b *nodeBuilderImpl) symbolTableToDeclarationStatements(symbolTable *ast.Sy panic("unimplemented") // !!! } +// getJSDocTypeAssertionType extracts the type node from a JSDoc type assertion. +// Currently stubbed since ast.IsJSDocTypeAssertion always returns false. +func getJSDocTypeAssertionType(node *ast.Node) *ast.TypeNode { + // !!! TODO: Implement when JSDoc type assertions are fully supported + return nil +} + func (b *nodeBuilderImpl) serializeTypeForExpression(expr *ast.Node) *ast.Node { // !!! TODO: shim, add node reuse t := b.ch.instantiateType(b.ch.getWidenedType(b.ch.getRegularTypeOfExpression(expr)), b.ctx.mapper) @@ -1987,6 +1994,43 @@ func (b *nodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati if symbol == nil { symbol = b.ch.getSymbolOfDeclaration(declaration) } + + // Check for PropertyAssignment with type assertion that can be reused (PR #60576) + if declaration != nil && declaration.Kind == ast.KindPropertyAssignment { + initializer := declaration.AsPropertyAssignment().Initializer + var assertionNode *ast.TypeNode + + // Check if initializer is a JSDoc type assertion + if ast.IsJSDocTypeAssertion(initializer) { + assertionNode = getJSDocTypeAssertionType(initializer) + } else if initializer.Kind == ast.KindAsExpression || initializer.Kind == ast.KindTypeAssertionExpression { + // Extract the type node from AsExpression or TypeAssertion + if initializer.Kind == ast.KindAsExpression { + assertionNode = initializer.AsAsExpression().Type + } else { + assertionNode = initializer.AsTypeAssertion().Type + } + } + + // If we have an assertion node, check if we can reuse it + if assertionNode != nil && !isConstTypeReference(assertionNode) { + // Try to reuse the existing type node if it's equivalent + if t == nil { + t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] + if t == nil && symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) { + t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper) + } + } + + if t != nil { + reusedNode := b.tryReuseExistingTypeNode(assertionNode, t, declaration, false) + if reusedNode != nil { + return reusedNode + } + } + } + } + if t == nil { t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] if t == nil { From 5569d5d5816ad574fc7003dd23e4bbc7ffd8e5d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 22:11:26 +0000 Subject: [PATCH 3/3] Refactor PropertyAssignment node reuse logic based on code review Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- internal/checker/nodebuilderimpl.go | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/internal/checker/nodebuilderimpl.go b/internal/checker/nodebuilderimpl.go index 240e35af16..c0daa32469 100644 --- a/internal/checker/nodebuilderimpl.go +++ b/internal/checker/nodebuilderimpl.go @@ -1644,6 +1644,21 @@ func getJSDocTypeAssertionType(node *ast.Node) *ast.TypeNode { return nil } +// getTypeForDeclaration resolves the type for a declaration, considering various special cases. +func (b *nodeBuilderImpl) getTypeForDeclaration(symbol *ast.Symbol, declaration *ast.Declaration) *Type { + t := b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] + if t == nil { + if symbol.Flags&ast.SymbolFlagsAccessor != 0 && declaration != nil && declaration.Kind == ast.KindSetAccessor { + t = b.ch.instantiateType(b.ch.getWriteTypeOfSymbol(symbol), b.ctx.mapper) + } else if symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) { + t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper) + } else { + t = b.ch.errorType + } + } + return t +} + func (b *nodeBuilderImpl) serializeTypeForExpression(expr *ast.Node) *ast.Node { // !!! TODO: shim, add node reuse t := b.ch.instantiateType(b.ch.getWidenedType(b.ch.getRegularTypeOfExpression(expr)), b.ctx.mapper) @@ -1998,28 +2013,22 @@ func (b *nodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati // Check for PropertyAssignment with type assertion that can be reused (PR #60576) if declaration != nil && declaration.Kind == ast.KindPropertyAssignment { initializer := declaration.AsPropertyAssignment().Initializer + initializerKind := initializer.Kind var assertionNode *ast.TypeNode // Check if initializer is a JSDoc type assertion if ast.IsJSDocTypeAssertion(initializer) { assertionNode = getJSDocTypeAssertionType(initializer) - } else if initializer.Kind == ast.KindAsExpression || initializer.Kind == ast.KindTypeAssertionExpression { + } else if initializerKind == ast.KindAsExpression || initializerKind == ast.KindTypeAssertionExpression { // Extract the type node from AsExpression or TypeAssertion - if initializer.Kind == ast.KindAsExpression { - assertionNode = initializer.AsAsExpression().Type - } else { - assertionNode = initializer.AsTypeAssertion().Type - } + assertionNode = getAssertedTypeNode(initializer) } // If we have an assertion node, check if we can reuse it if assertionNode != nil && !isConstTypeReference(assertionNode) { // Try to reuse the existing type node if it's equivalent if t == nil { - t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] - if t == nil && symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) { - t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper) - } + t = b.getTypeForDeclaration(symbol, declaration) } if t != nil { @@ -2032,16 +2041,7 @@ func (b *nodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati } if t == nil { - t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] - if t == nil { - if symbol.Flags&ast.SymbolFlagsAccessor != 0 && declaration.Kind == ast.KindSetAccessor { - t = b.ch.instantiateType(b.ch.getWriteTypeOfSymbol(symbol), b.ctx.mapper) - } else if symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) { - t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper) - } else { - t = b.ch.errorType - } - } + t = b.getTypeForDeclaration(symbol, declaration) } // !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker