Skip to content

Comments

Improve type inference of Result<T, E>#3506

Open
henrymercer wants to merge 3 commits intomainfrom
henrymercer/result-better-inference
Open

Improve type inference of Result<T, E>#3506
henrymercer wants to merge 3 commits intomainfrom
henrymercer/result-better-inference

Conversation

@henrymercer
Copy link
Contributor

Thanks @mbg for pointing out this drawback.

By defining Result<T, E> as a sum type Success<T> | Failure<E>, we can now infer that a Result is a failure if it is not a success. For example, if we have:

const a = new Success(2) as Result<number, string>;
if (a.isSuccess()) {
  logger.info(`Success with value ${a.value}`);
} else {
  logger.info(`Failure with error ${a.value}`);
}

we can now infer in the else branch that a.value must be a string.

@henrymercer henrymercer requested a review from a team as a code owner February 24, 2026 17:43
Copilot AI review requested due to automatic review settings February 24, 2026 17:43
@github-actions github-actions bot added the size/S Should be easy to review label Feb 24, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request refactors the Result<T, E> type from a single class with static factory methods to a discriminated union of Success<T> and Failure<E> classes. This improves TypeScript's type inference, allowing the compiler to correctly narrow types in conditional branches when checking isSuccess() or isFailure().

Changes:

  • Replaced the single Result class with two separate classes (Success and Failure) that form a discriminated union
  • Updated all call sites from Result.success() and Result.failure() to use new Success() and new Failure() constructors
  • Updated tests to reflect the new API

Reviewed changes

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

File Description
src/util.ts Refactored Result<T, E> from a single class to a sum type with Success<T> and Failure<E> classes implementing a common ResultLike interface
src/util.test.ts Updated tests to use new Success() and new Failure() instead of static factory methods
src/init-action.ts Updated imports and usages to construct Success and Failure instances directly
lib/init-action.js Generated JavaScript code mirroring the TypeScript changes
Comments suppressed due to low confidence (1)

src/util.test.ts:567

  • The test description still references "Result.success" but the implementation now uses "new Success()". Consider updating the test description to match the new API, for example: "Success creates a success result".
test("Result.success creates a success result", (t) => {

mbg
mbg previously approved these changes Feb 24, 2026
Copy link
Member

@mbg mbg left a comment

Choose a reason for hiding this comment

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

LGTM with one minor comment. Also, was there a particular advantage you found to the class-based approach over something like:

type Success<T> = { isSuccess: true, value: T };
type Failure<E> = { isSuccess: false, value: E };
type Result<T, E> = Success<T> | Failure<E>;

Co-authored-by: Michael B. Gale <mbg@github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Should be easy to review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants