Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More ergonomic match #497

Merged
merged 8 commits into from
Aug 12, 2024
Merged

More ergonomic match #497

merged 8 commits into from
Aug 12, 2024

Conversation

braxtonhall
Copy link
Contributor

@braxtonhall braxtonhall commented Oct 9, 2023

When writing a match in neverthrow, the ok and err callbacks must have the same ReturnType in their signature. This means the following code results in an error

declare const result: Result<string, number>
const matchResult = result.match(
	(value: string) => !!value,
	(value: number) => BigInt(10),
//	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TS2345: Argument of type  (value: number) => bigint  is not assignable to parameter of type  (e: number) => boolean 
// Type  bigint  is not assignable to type  boolean
)

This can be fixed by annotating the callbacks or the match application with more generic types

declare const result: Result<string, number>
const matchResultA = result.match(
	(value: string): boolean | bigint => !!value,
	(value: number) => BigInt(10),
)
const matchResultB = result.match<boolean | bigint>(
	(value: string) => !!value,
	(value: number) => BigInt(10),
)

However this is pretty inconvenient!

Instead, match can infer that the result is the union of whatever ok returns, and whatever err returns

  interface Result<T, E> {
    // ...
-   match<A>(ok: (t: T) => A, err: (e: E) => A): A;
+   match<A, B>(ok: (t: T) => A, err: (e: E) => B): A | B;
    // ...
  }
declare const result: Result<string, number>
const matchResult = result.match(
	(value: string) => !!value,
	(value: number) => BigInt(10),
)

true satisfies typeof matchResult
BigInt(10) satisfies typeof matchResult

Unfortunately, this would be a breaking change! Anyone who had already annotated their method call would now get an error

declare const result: Result<string, number>
const matchResult = result.match<boolean | bigint>(
//                               ~~~~~~~~~~~~~~~~
// TS2558: Expected  2  type arguments, but got  1 
	(value: string) => !!value,
	(value: number) => BigInt(10),
)

To support legacy code, we can add a default to the second type argument

  interface Result<T, E> {
    // ...
-   match<A>(ok: (t: T) => A, err: (e: E) => A): A;
+   match<A, B = A>(ok: (t: T) => A, err: (e: E) => B): A | B;
    // ...
  }

Copy link

changeset-bot bot commented Jul 29, 2024

🦋 Changeset detected

Latest commit: 1f87908

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
neverthrow Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Owner

@supermacro supermacro left a comment

Choose a reason for hiding this comment

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

Thank you 🚀

@supermacro supermacro merged commit 3960060 into supermacro:master Aug 12, 2024
8 checks passed
@github-actions github-actions bot mentioned this pull request Aug 12, 2024
@braxtonhall braxtonhall deleted the match branch August 20, 2024 15:14
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.

3 participants