Skip to content

Commit 16adc4e

Browse files
authored
Merge pull request #3506 from github/henrymercer/result-better-inference
Improve type inference of `Result<T, E>`
2 parents 28737ec + 2a607fe commit 16adc4e

File tree

4 files changed

+71
-58
lines changed

4 files changed

+71
-58
lines changed

lib/init-action.js

Lines changed: 21 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/init-action.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ import {
9696
GitHubVersion,
9797
Result,
9898
getOptionalEnvVar,
99+
Success,
100+
Failure,
99101
} from "./util";
100102
import { checkWorkflow } from "./workflow";
101103

@@ -834,25 +836,25 @@ async function loadRepositoryProperties(
834836
"Skipping loading repository properties because the repository is owned by a user and " +
835837
"therefore cannot have repository properties.",
836838
);
837-
return Result.success({});
839+
return new Success({});
838840
}
839841

840842
if (!(await features.getValue(Feature.UseRepositoryProperties))) {
841843
logger.debug(
842844
"Skipping loading repository properties because the UseRepositoryProperties feature flag is disabled.",
843845
);
844-
return Result.success({});
846+
return new Success({});
845847
}
846848

847849
try {
848-
return Result.success(
850+
return new Success(
849851
await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo),
850852
);
851853
} catch (error) {
852854
logger.warning(
853855
`Failed to load repository properties: ${getErrorMessage(error)}`,
854856
);
855-
return Result.failure(error);
857+
return new Failure(error);
856858
}
857859
}
858860

src/util.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -564,27 +564,27 @@ test("joinAtMost - truncates list if array is > than limit", (t) => {
564564
t.false(result.includes("test6"));
565565
});
566566

567-
test("Result.success creates a success result", (t) => {
568-
const result = util.Result.success("test value");
567+
test("Success creates a success result", (t) => {
568+
const result = new util.Success("test value");
569569
t.true(result.isSuccess());
570570
t.false(result.isFailure());
571571
t.is(result.value, "test value");
572572
});
573573

574-
test("Result.failure creates a failure result", (t) => {
574+
test("Failure creates a failure result", (t) => {
575575
const error = new Error("test error");
576-
const result = util.Result.failure(error);
576+
const result = new util.Failure(error);
577577
t.false(result.isSuccess());
578578
t.true(result.isFailure());
579579
t.is(result.value, error);
580580
});
581581

582-
test("Result.orElse returns the value for a success result", (t) => {
583-
const result = util.Result.success("success value");
582+
test("Success.orElse returns the value for a success result", (t) => {
583+
const result = new util.Success("success value");
584584
t.is(result.orElse("default value"), "success value");
585585
});
586586

587-
test("Result.orElse returns the default value for a failure result", (t) => {
588-
const result = util.Result.failure(new Error("test error"));
587+
test("Failure.orElse returns the default value for a failure result", (t) => {
588+
const result = new util.Failure(new Error("test error"));
589589
t.is(result.orElse("default value"), "default value");
590590
});

src/util.ts

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,42 +1291,51 @@ export function joinAtMost(
12911291
return array.join(separator);
12921292
}
12931293

1294-
/** A success result. */
1295-
type Success<T> = Result<T, never>;
1296-
/** A failure result. */
1297-
type Failure<E> = Result<never, E>;
1294+
/** An interface representing something that is either a success or a failure. */
1295+
interface ResultLike<T, E> {
1296+
/** The value of the result, which can be either a success value or a failure value. */
1297+
value: T | E;
1298+
/** Whether this result represents a success. */
1299+
isSuccess(): this is Success<T>;
1300+
/** Whether this result represents a failure. */
1301+
isFailure(): this is Failure<E>;
1302+
/** Get the value if this is a success, or return the default value if this is a failure. */
1303+
orElse<U>(defaultValue: U): T | U;
1304+
}
12981305

1299-
/**
1300-
* A simple result type representing either a success or a failure.
1301-
*/
1302-
export class Result<T, E> {
1303-
private constructor(
1304-
private readonly _ok: boolean,
1305-
public readonly value: T | E,
1306-
) {}
1307-
1308-
/** Creates a success result. */
1309-
static success<T>(value: T): Success<T> {
1310-
return new Result(true, value) as Success<T>;
1306+
/** A simple result type representing either a success or a failure. */
1307+
export type Result<T, E> = Success<T> | Failure<E>;
1308+
1309+
/** A result representing a success. */
1310+
export class Success<T> implements ResultLike<T, never> {
1311+
constructor(public readonly value: T) {}
1312+
1313+
isSuccess(): this is Success<T> {
1314+
return true;
13111315
}
13121316

1313-
/** Creates a failure result. */
1314-
static failure<E>(value: E): Failure<E> {
1315-
return new Result(false, value) as Failure<E>;
1317+
isFailure(): this is Failure<never> {
1318+
return false;
13161319
}
13171320

1318-
/** Whether this result represents a success. */
1319-
isSuccess(): this is Success<T> {
1320-
return this._ok;
1321+
orElse<U>(_defaultValue: U): T {
1322+
return this.value;
1323+
}
1324+
}
1325+
1326+
/** A result representing a failure. */
1327+
export class Failure<E> implements ResultLike<never, E> {
1328+
constructor(public readonly value: E) {}
1329+
1330+
isSuccess(): this is Success<never> {
1331+
return false;
13211332
}
13221333

1323-
/** Whether this result represents a failure. */
13241334
isFailure(): this is Failure<E> {
1325-
return !this._ok;
1335+
return true;
13261336
}
13271337

1328-
/** Get the value if this is a success, or return the default value if this is a failure. */
1329-
orElse<U>(defaultValue: U): T | U {
1330-
return this.isSuccess() ? this.value : defaultValue;
1338+
orElse<U>(defaultValue: U): U {
1339+
return defaultValue;
13311340
}
13321341
}

0 commit comments

Comments
 (0)