Result object and Error Handling
Result object
The Result class is similar to Option type/object in Scala/Rust.
It wraps up some value
and error
- so that a function can return both simulateously.
This is similar to the https://www.npmjs.com/package/neverthrow package
See lib\Core\Result.ts
for more documentation.
Simple usage
function someFn(someArg: boolean): Result<string | undefined> {
if (someArg) {
return new Result("success");
}
return new Result(undefined, TerriaError.from("SOME ERROR"));
}
This can now be used in a few different ways
Ignore error and just return value
const value = someFn(true).ignoreError();
// value = "success"
const value = someFn(false).ignoreError();
// value = undefined
Catch error, do something with it and then return value
const value = someFn(someArg).catchError((error) =>
doSomethingWithError(error)
);
Throw error OR return value
const value = someFn(someArg).throwIfError();
Throw if value is undefined, otherwise return value
const value = someFn(someArg).throwIfUndefined();
Raise error to user if error, and return value
const value = someFn(someArg).raiseError(terria);
TerriaErrorOverrides
TerriaErrorOverrides
can be provided when creating a new Result()
, and also methods which act on errors (eg raiseError
, throwIfError
, ...)
This allows you to add more context to TerriaErrors.
Valid TerriaErrorOverrides
include:
- String values - eg
"Some error message"
- JSON representation of
TerriaError
- eg{"title": "Error title", "message": "Some error message"}
Simple usage
function someFn(someArg): Result<string | undefined> {
if (someArg) {
return new Result("success");
}
// Here we create a TerriaError with message "Some Error inside result"
return new Result(undefined, TerriaError.from("Some error inside result"));
}
// Here we add `TerriaErrorOverrides` in throwIfError.
// This is equivalent to calling `createParentError()` on the error inside inside result.
// Eg. error.createParentError("Some error outside of result")
const value = someFn(someArg).throwIfError("Some error outside of result");
This will now throw a chain of TerriaErrors - which provides a good stack trace:
{
...,
message: "Some error outside of result",
originalError: {
...,
message: "Some error inside result"
}
}
Convenience functions
There are a few convenience functions and methods to make Result
a bit easier to use.
Result.error()
This accepts same arguments as TerriaError.from
and will create a new Result
object with an error
Result.none()
This also accepts same arguments as TerriaError.from
- but all arguments are optional. It will create a Result with no value (and potentially an error - if arguments are defined)
Result.combine()
Combines an array of Result
objects into a single Result
- also accepts same arguments as TerriaError.from
.
TerriaError object
Represents an error that occurred in a TerriaJS module, especially an asynchronous one that cannot be raised by throwing an exception because no one would be able to catch it.
See lib\Core\TerriaError.ts
for more documentation.
TerriaErrorSeverity
TerriaErrorSeverity
enum values can be Error
or Warning
.
- Errors with severity
Error
are presented to the user.Warning
will just be printed to console. - By default, errors will use
Error
TerriaErrorSeverity
will be copied through nestedTerriaErrors
on creation (eg if you callTerriaError.from()
on aWarning
then the parent error will also beWarning
)- Loading models from share links or stories will use
Warning
if the model is not in the workbench, otherwise it will useError
.
Example of severity propagation
Say we have this error with severity Warning
:
const error = {
message: "some message",
severity: TerriaErrorSeverity.Warning
};
And then we call:
error.createParentError("some higher message");
It will return a new TerriaError with the same severity - Warning
- not the default severity Error
.
{
"message": "some higher message",
"severity": TerriaErrorSeverity.Warning
"orginalError": {
"message": "some message",
"severity": TerriaErrorSeverity.Warning
}
}