Skip to content

Add Infer function return type refactoring code action - #4749

Open
xeho91 wants to merge 19 commits into
microsoft:mainfrom
xeho91:xeho91/lsp-refactor-infer-return-type
Open

Add Infer function return type refactoring code action#4749
xeho91 wants to merge 19 commits into
microsoft:mainfrom
xeho91:xeho91/lsp-refactor-infer-return-type

Conversation

@xeho91

@xeho91 xeho91 commented Jul 26, 2026

Copy link
Copy Markdown

Discussion #4747
Implementing one of the missing refactoring code action. I created a basic infrastructure, especially for testing with fourslash.

This is my first contribution. I'm new to Golang, and I used AI to support myself.
I reviewed and tested manually myself the refactoring code action on my personal playground project.

I would like to continue handling any further code review feedback by myself - will be happy to continue contributing!

Copilot AI review requested due to automatic review settings July 26, 2026 16:24
@xeho91

xeho91 commented Jul 26, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an LSP refactoring that infers and inserts TypeScript function return types.

Changes:

  • Registers and advertises the refactor code-action kind.
  • Implements return-type inference and edit generation.
  • Adds fourslash helpers and availability tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/lsp/server.go Advertises rewrite refactoring support.
internal/ls/codeactions.go Adds refactor providers and LSP conversion.
internal/ls/codeactions_refactor_inferreturntype.go Implements return-type inference.
internal/fourslash/fourslash.go Adds refactor availability assertions.
internal/fourslash/tests/refactorInferReturnType_test.go Tests refactor availability.
Comments suppressed due to low confidence (2)

internal/ls/codeactions_refactor_inferreturntype.go:126

  • An inferred type predicate must be emitted as the full predicate, not just its target type. For function isString(x: unknown) { return typeof x === "string"; }, this path turns the inferred x is string into : string, which both loses narrowing and conflicts with the boolean body. Use TypePredicateToTypePredicateNode and carry that node through to the edit, as the existing isolated-declarations fixer does.
		typePredicate := ch.GetTypePredicateOfSignature(signature)
		if typePredicate != nil && typePredicate.Type() != nil {
			return typePredicate.Type()

internal/ls/codeactions_refactor_inferreturntype.go:156

  • The ancestor walk crosses function-body boundaries, so the refactor is offered at arbitrary positions inside a function (including inside an arrow's body). The TypeScript reference deliberately quits at a block or at an arrow body/=> boundary; apply the same boundary checks here so the action remains contextual to the declaration.
		node = node.Parent

Comment thread internal/ls/codeactions_refactor_inferreturntype.go
Comment thread internal/ls/codeactions.go
Comment thread internal/fourslash/tests/refactorInferReturnType_test.go Outdated
@xeho91
xeho91 force-pushed the xeho91/lsp-refactor-infer-return-type branch from 5c01a6a to ef5db47 Compare July 26, 2026 16:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

internal/ls/codeactions.go:194

  • The refactor category gate does not filter individual actions by Context.Only: every registered action is invoked here, and RefactorAction.Kinds is never consulted. A request for only refactor.extract therefore still receives this refactor.rewrite.function.returnType action, contrary to LSP only semantics. Match each requested kind against the action's advertised kinds before calling its factory.
		for _, provider := range refactorProviders {
			for _, action := range provider.RefactorActions {
				providerActions, err := action.GetActions(ctx, refactorContext, action.ID)

Comment thread internal/ls/codeactions_refactor_inferreturntype.go Outdated
Comment thread internal/ls/codeactions_refactor_inferreturntype.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

internal/ls/codeactions.go:194

  • The new Kinds metadata is never consulted, so an LSP request with only: ["refactor.extract"] still runs this rewrite provider and returns refactor.rewrite.function.returnType. only must be matched against each action's declared kinds (requested kind equals or contains an action kind) before calling GetActions; this also needs a filter-specific test.
		for _, provider := range refactorProviders {
			for _, action := range provider.RefactorActions {
				providerActions, err := action.GetActions(ctx, refactorContext, action.ID)

Comment thread internal/ls/codeactions_refactor_inferreturntype.go
Comment thread internal/ls/codeactions_refactor_inferreturntype.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

internal/ls/codeactions_refactor_inferreturntype.go:131

  • The predicate path still omits InternalFlagsAllowUnresolvedNames: Checker.TypePredicateToTypePredicateNode hardcodes InternalFlagsNone (internal/checker/printer.go:483-485), while unresolved computed members are dropped without the flag (internal/checker/nodecopy.go:648-657). Consequently, an inferred predicate can receive an incomplete target type even though the normal and overload paths now preserve these names. Add an overload that accepts internal flags and pass AllowUnresolvedNames, matching the upstream refactor.
			return ch.TypePredicateToTypePredicateNode(typePredicate, enclosingDecl, nodebuilder.FlagsNoTruncation, idToSymbol)

Comment thread internal/ls/codeactions.go
Comment thread internal/ls/codeactions_refactor_inferreturntype.go Outdated
Comment thread internal/fourslash/fourslash.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

internal/ls/codeactions_refactor_inferreturntype.go:170

  • The same descendant lookup offers the action from a class property name, such as class C { f/*caret*/ = () => 1 }, although the caret is not within the arrow function. This differs from the reference implementation’s ancestor-only applicability check. Restrict the fallback to positions inside the initializer, or remove it if unnecessary.
		if ast.IsPropertyDeclaration(node) {
			if init := node.AsPropertyDeclaration().Initializer; init != nil && convertibleDeclaration(init) {
				return init
			}
		}

internal/ls/codeactions_refactor_inferreturntype.go:131

  • This predicate conversion still omits InternalFlagsAllowUnresolvedNames: Checker.TypePredicateToTypePredicateNode hard-codes InternalFlagsNone in internal/checker/printer.go:483-485, while the reference infer-return-type implementation explicitly passes the allow-unresolved flag. Consequently, inferred predicates whose narrowed type contains unresolved/computed members can be dropped or rendered incompletely. Add an Ex variant that accepts internal flags and use it here, matching the normal and overload paths.
			return ch.TypePredicateToTypePredicateNode(typePredicate, enclosingDecl, nodebuilder.FlagsNoTruncation, idToSymbol)

Comment thread internal/ls/codeactions_refactor_inferreturntype.go Outdated
Comment thread internal/ls/codeactions.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

internal/ls/codeactions_refactor_inferreturntype.go:131

  • The predicate conversion still uses checker.TypePredicateToTypePredicateNode, whose implementation hardcodes InternalFlagsNone (internal/checker/printer.go:483-485). Unlike the normal and overload paths above—and the TypeScript reference—this can drop unresolved computed members from the narrowed type (internal/checker/nodecopy.go:655-657), producing an incomplete x is {}-style annotation. Add an Ex variant that accepts internal flags, pass InternalFlagsAllowUnresolvedNames here, and cover a predicate containing such a computed member.
			return ch.TypePredicateToTypePredicateNode(typePredicate, enclosingDecl, nodebuilder.FlagsNoTruncation, idToSymbol)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread internal/ls/codeactions.go Outdated
Comment thread internal/ls/codeactions_refactor_inferreturntype.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment thread internal/ls/codeactions_refactor_inferreturntype.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants