Skip to content

Commit 160d27b

Browse files
committed
Improve type inference of Result<T, E>
1 parent dc00a6f commit 160d27b

File tree

4 files changed

+67
-54
lines changed

4 files changed

+67
-54
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: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,26 +565,26 @@ test("joinAtMost - truncates list if array is > than limit", (t) => {
565565
});
566566

567567
test("Result.success creates a success result", (t) => {
568-
const result = util.Result.success("test value");
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

574574
test("Result.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

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

587587
test("Result.orElse returns the default value for a failure result", (t) => {
588-
const result = util.Result.failure(new Error("test error"));
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
@@ -1295,42 +1295,51 @@ export function joinAtMost(
12951295
return array.join(separator);
12961296
}
12971297

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

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

1317-
/** Creates a failure result. */
1318-
static failure<E>(value: E): Failure<E> {
1319-
return new Result(false, value) as Failure<E>;
1321+
isFailure(): this is Failure<never> {
1322+
return false;
13201323
}
13211324

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

1327-
/** Whether this result represents a failure. */
13281338
isFailure(): this is Failure<E> {
1329-
return !this._ok;
1339+
return true;
13301340
}
13311341

1332-
/** Get the value if this is a success, or return the default value if this is a failure. */
1333-
orElse<U>(defaultValue: U): T | U {
1334-
return this.isSuccess() ? this.value : defaultValue;
1342+
orElse<U>(defaultValue: U): never | U {
1343+
return defaultValue;
13351344
}
13361345
}

0 commit comments

Comments
 (0)