Implement a Generic Result Type in TypeScript
Functional programming paradigms often utilize a Result type to represent the outcome of an operation that can either succeed with a value or fail with an error. This is a powerful pattern for handling errors in a more explicit and manageable way compared to traditional exception handling. Your task is to implement a generic Result type in TypeScript that encapsulates these success and failure states.
Problem Description
You need to create a TypeScript discriminated union (or a similar pattern) that represents a Result. This Result type should be generic and accept two type parameters: T for the success type and E for the error type.
The Result type should have two distinct variants:
Ok<T>: Represents a successful operation, holding a value of typeT.Err<E>: Represents a failed operation, holding an error value of typeE.
You will also need to implement methods on this Result type to allow for:
- Checking if the
Resultis anOkor anErr. - Extracting the value if it's
Ok, or providing a default if it'sErr. - Mapping a function over the success value if the
ResultisOk. - Handling both
OkandErrcases gracefully with callback functions.
Key Requirements:
- Define a generic
Resulttype that can hold either a success value (T) or an error value (E). - Provide mechanisms to distinguish between
OkandErrstates. - Implement methods to safely access the success value or handle the error.
- Implement a
mapmethod that applies a function to the success value if present.
Expected Behavior:
- When an operation is successful, it should return an
Okvariant containing the result. - When an operation fails, it should return an
Errvariant containing the error information. - Methods should behave predictably based on whether the
ResultisOkorErr.
Edge Cases:
- What happens if you try to access the value of an
Err? - What happens if you try to map a function over an
Err? - Consider
nullorundefinedas potentialTorEtypes.
Examples
Example 1: Basic Usage
// Assume a function that might succeed or fail
function divide(a: number, b: number): Result<number, string> {
if (b === 0) {
return Err("Division by zero is not allowed.");
}
return Ok(a / b);
}
const successResult = divide(10, 2); // Ok<number>
const failureResult = divide(10, 0); // Err<string>
// Checking the state
if (successResult.isOk()) {
console.log("Success:", successResult.unwrap()); // Output: Success: 5
}
if (failureResult.isErr()) {
console.log("Error:", failureResult.unwrapErr()); // Output: Error: Division by zero is not allowed.
}
Example 2: Using map
const result = Ok(5);
const mappedResult = result.map(x => x * 2); // Ok<number>
const errorResult = Err("Something went wrong");
const mappedErrorResult = errorResult.map(x => x * 2); // Err<string>
console.log(mappedResult.unwrap()); // Output: 10
console.log(mappedErrorResult.unwrapErr()); // Output: Something went wrong
Example 3: Using match (or similar pattern)
function processResult(res: Result<number, string>): string {
return res.match({
Ok: (value) => `Processed value: ${value}`,
Err: (error) => `Encountered error: ${error}`
});
}
console.log(processResult(Ok(100))); // Output: Processed value: 100
console.log(processResult(Err("Network error"))); // Output: Encountered error: Network error
Constraints
- The implementation must be entirely in TypeScript.
- The
Resulttype should be generic (Result<T, E>). - The implementation should aim for clarity and maintainability.
- There are no strict performance constraints, but avoid overly inefficient operations.
Notes
- Consider using discriminated unions for the
OkandErrvariants. - Think about how to make the
unwrap()andunwrapErr()methods throw specific errors when called on the wrong variant to signal programmer error. - The
matchmethod (or a similar approach) is a common functional pattern for handling both cases of aResult. You can name it something else if you prefer, but its functionality should be to take two functions (one forOk, one forErr) and return the result of whichever function is appropriate. - You might want to create helper functions or static methods to construct
OkandErrinstances.