terriajs
Version:
Geospatial data visualization platform.
175 lines • 5.65 kB
JavaScript
import isDefined from "./isDefined";
import TerriaError, { parseOverrides } from "./TerriaError";
/**
* 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
*
* ## Simple usage:
*
* ```ts
* function someFn(someArg): 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
*
* ```ts
* const value = someFn(someArg).ignoreError()
* ```
*
* #### Catch error, do something with it and then return value
*
* ```ts
* const value = someFn(someArg).catchError((error) => doSomethingWithError(error))
* ```
*
* #### Throw error OR return value
*
* ```ts
* const value = someFn(someArg).throwIfError()
* ```
* #### Throw if value is undefined, otherwise return value
*
* ```ts
* const value = someFn(someArg).throwIfUndefined()
* ```
*
* ## `TerriaErrorOverrides`:
*
* `TerriaErrorOverrides` can be provided when creating a `new Result()`, and also when using `throwIfError()` or `throwIfUndefined`.
* This allows you to add more context to TerriaErrors.
*
* ### Simple usage:
*
* ```ts
* function someFn(someArg): Result<string | undefined> {
* if (someArg) {
* return new Result('success')
* }
* return new Result(undefined, TerriaError.from("Some error inside function"))
* }
*
* const value = someFn(someArg).throwIfError("Some error outside of function")
* ```
*
* This will now throw a chain of TerriaErrors - which provides a good stack trace:
*
* ```json
* {
* ...,
* message: "Some error outside of function",
* originalError: {
* ...,
* message: "Some error inside function"
* }
* }
* ```
*/
export default class Result {
/** Convenience constructor to return a Result with an error.
*
* This accepts same arguments as `TerriaError.from`
*/
static error(error, overrides) {
return new Result(undefined, TerriaError.from(error, overrides));
}
/** Convenience constructor to return a Result with no value (and potentially an error) */
static none(error, overrides) {
return error ? Result.error(error, overrides) : new Result(undefined);
}
/** Combine array of Results.
* The new Result will have an error if at least one error has occurred in array of results
* The value will be array of result values.
*
*/
static combine(results, errorOverrides) {
return new Result(results.map((r) => r.value), TerriaError.combine(results.map((r) => r.error), errorOverrides));
}
value;
_error;
constructor(value, error) {
this.value = value;
this._error = error
? // Create TerriaError if error is TerriaErrorOptions
error instanceof TerriaError
? error
: new TerriaError(error)
: undefined;
}
get error() {
return this._error;
}
/** Returns value regardless if an error occurred */
ignoreError() {
return this.value;
}
/** Apply callback function if an error occurred, and then return value */
catchError(callback) {
if (this._error)
callback(this._error);
return this.value;
}
/** Log error to console if one has occurred, and then return value.
*
* @param errorOverrides can be used to add error context
*/
logError(errorOverrides) {
if (this._error)
TerriaError.from(this._error, errorOverrides).log();
return this.value;
}
/** Raise error if one has occurred, and then return value.
*
* @param errorOverrides can be used to add error context
* @param forceRaiseToUser true to force show error to user
*/
raiseError(terria, errorOverrides, forceRaiseToUser) {
if (this._error)
terria.raiseErrorToUser(this.error, errorOverrides, forceRaiseToUser);
return this.value;
}
/** Throw error if one occurred, otherwise return value.
*
* @param errorOverrides can be used to add error context
*/
throwIfError(errorOverrides) {
if (this._error)
throw TerriaError.from(this._error, errorOverrides);
return this.value;
}
/** Throw error if value is undefined (regardless of if an error has occurred), otherwise return value.
*
* @param errorOverrides will be used to create error if value is undefined
*/
throwIfUndefined(errorOverrides) {
if (isDefined(this.value))
return this.value;
// If value is undefined, throw error
throw this._error
? TerriaError.from(this._error, errorOverrides)
: new TerriaError({
message: "Unhandled required Result exception",
...parseOverrides(errorOverrides)
});
}
pushErrorTo(errors, errorOverrides) {
if (this._error)
errors.push(TerriaError.from(this._error, errorOverrides));
return this.value;
}
/** Clone this `Result` and apply `TerriaErrorOverrides` if there is an error */
clone(errorOverrides) {
if (this._error)
return new Result(this.value, TerriaError.from(this._error, errorOverrides));
return new Result(this.value);
}
}
//# sourceMappingURL=Result.js.map