#Importing Result
@praha/byethrow provides two distinct import methods to balance efficient development with learnability.
Both approaches fully support tree-shaking, ensuring that unused features are automatically excluded from your final bundle.
#Two Import Methods
#Explicit Namespace Approach (Result)
For verbose and explicit code that prioritizes clarity, use the Result namespace:
import { import Result Result } from '@praha/byethrow';
const const validateUser: (id: string) => Result.Failure<Error> | Result.Success<string> validateUser = (id: string id : string) => {
if (!id: string id .String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.
startsWith ('u')) {
return import Result Result .const fail: <Error>(error: Error) => Result.Result<never, Error> (+1 overload) fail (new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error ('Invalid ID format'));
}
return import Result Result .const succeed: <string>(value: string) => Result.Result<string, never> (+1 overload) succeed (id: string id );
};
const const result: Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>
result = import Result Result .const pipe: <Result.Result<"u123", never>, Result.Result<string, Error>, Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>>(a: Result.Result<"u123", never>, ab: (a: Result.Result<"u123", never>) => Result.Result<string, Error>, bc: (b: Result.Result<string, Error>) => Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>) => Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error> (+25 overloads)
pipe (
import Result Result .const succeed: <"u123">(value: "u123") => Result.Result<"u123", never> (+1 overload) succeed ('u123'),
import Result Result .const andThen: <Result.Result<"u123", never>, Result.Failure<Error> | Result.Success<string>>(fn: (a: "u123") => Result.Failure<Error> | Result.Success<string>) => (result: Result.Result<"u123", never>) => Result.Result<string, Error> (+1 overload) andThen (const validateUser: (id: string) => Result.Failure<Error> | Result.Success<string> validateUser ),
import Result Result .const map: <Result.Result<string, Error>, {
readonly id: string;
readonly name: "John Doe";
}>(fn: (a: string) => {
readonly id: string;
readonly name: "John Doe";
}) => (result: Result.Result<string, Error>) => Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error> (+1 overload)
map (id: string id => ({ id: string id , name: "John Doe" name : 'John Doe' }))
);
if (import Result Result .const isSuccess: <Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>>(result: Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>) => result is Result.Success<{
readonly id: string;
readonly name: "John Doe";
}>
Type guard to check if a
Result
is a
Success
.
@function@typeParamR - The type of the result to check.@paramresult - The Result to check.@returnstrue if the result is a Success , otherwise false.@exampleimport { Result } from '@praha/byethrow';
const result: Result.Result<number, string> = { type: 'Success', value: 10 };
if (Result.isSuccess(result)) {
console.log(result.value); // Safe access to value
}
@categoryType Guards isSuccess (const result: Result.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>
result )) {
var console: Console console .Console.log(...data: any[]): voidThe console.log() static method outputs a message to the console.
log (const result: Result.Success<{
readonly id: string;
readonly name: "John Doe";
}>
result .value: {
readonly id: string;
readonly name: "John Doe";
}
value );
}#Shorthand Alias (R)
For concise code that favors brevity, use the R alias:
import { import R R } from '@praha/byethrow';
const const validateUser: (id: string) => R.Failure<Error> | R.Success<string> validateUser = (id: string id : string) => {
if (!id: string id .String.startsWith(searchString: string, position?: number): booleanReturns true if the sequence of elements of searchString converted to a String is the
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.
startsWith ('u')) {
return import R R .const fail: <Error>(error: Error) => R.Result<never, Error> (+1 overload) fail (new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error ('Invalid ID format'));
}
return import R R .const succeed: <string>(value: string) => R.Result<string, never> (+1 overload) succeed (id: string id );
};
const const result: R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>
result = import R R .const pipe: <R.Result<"u123", never>, R.Result<string, Error>, R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>>(a: R.Result<"u123", never>, ab: (a: R.Result<"u123", never>) => R.Result<string, Error>, bc: (b: R.Result<string, Error>) => R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>) => R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error> (+25 overloads)
pipe (
import R R .const succeed: <"u123">(value: "u123") => R.Result<"u123", never> (+1 overload) succeed ('u123'),
import R R .const andThen: <R.Result<"u123", never>, R.Failure<Error> | R.Success<string>>(fn: (a: "u123") => R.Failure<Error> | R.Success<string>) => (result: R.Result<"u123", never>) => R.Result<string, Error> (+1 overload) andThen (const validateUser: (id: string) => R.Failure<Error> | R.Success<string> validateUser ),
import R R .const map: <R.Result<string, Error>, {
readonly id: string;
readonly name: "John Doe";
}>(fn: (a: string) => {
readonly id: string;
readonly name: "John Doe";
}) => (result: R.Result<string, Error>) => R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error> (+1 overload)
map (id: string id => ({ id: string id , name: "John Doe" name : 'John Doe' }))
);
if (import R R .const isSuccess: <R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>>(result: R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>) => result is R.Success<{
readonly id: string;
readonly name: "John Doe";
}>
Type guard to check if a
Result
is a
Success
.
@function@typeParamR - The type of the result to check.@paramresult - The Result to check.@returnstrue if the result is a Success , otherwise false.@exampleimport { Result } from '@praha/byethrow';
const result: Result.Result<number, string> = { type: 'Success', value: 10 };
if (Result.isSuccess(result)) {
console.log(result.value); // Safe access to value
}
@categoryType Guards isSuccess (const result: R.Result<{
readonly id: string;
readonly name: "John Doe";
}, Error>
result )) {
var console: Console console .Console.log(...data: any[]): voidThe console.log() static method outputs a message to the console.
log (const result: R.Success<{
readonly id: string;
readonly name: "John Doe";
}>
result .value: {
readonly id: string;
readonly name: "John Doe";
}
value );
}#Tree-Shaking Support
@praha/byethrow achieves complete tree-shaking support:
// Example: Usage in a small application
import { import R R } from '@praha/byethrow';
// Only these features are actually used
const const parseNumber: (input: string) => R.Result<number, Error> parseNumber = import R R .fn<(input: string) => number, Error>(options: {
try: (input: string) => number;
catch: (error: unknown) => Error;
}): (input: string) => R.Result<number, Error> (+3 overloads)
export fn
Wraps a function that may throw and returns a new function that returns a
Result
or
ResultAsync
.
You can use either a custom catch handler or rely on the safe: true option
to assume the function cannot throw.
@function@typeParamT - The function type to execute (sync or async) or a Promise type.@typeParamE - The error type to return if catch is used.@returnsA new function that returns a Result or ResultAsync wrapping the original function's return value or the caught error.@exampleSync try-catch
import { Result } from '@praha/byethrow';
const fn = Result.fn({
try: (x: number) => {
if (x < 0) throw new Error('Negative!');
return x * 2;
},
catch: (error) => new Error('Oops!', { cause: error }),
});
const result = fn(5); // Result.Result<number, Error>
@exampleSync safe
import { Result } from '@praha/byethrow';
const fn = Result.fn({
safe: true,
try: (x: number) => x + 1,
});
const result = fn(1); // Result.Result<number, never>
@exampleAsync try-catch
import { Result } from '@praha/byethrow';
const fn = Result.fn({
try: async (id: string) => await fetch(`/api/data/${id}`),
catch: (error) => new Error('Oops!', { cause: error }),
});
const result = await fn('abc'); // Result.ResultAsync<Response, Error>
@exampleAsync safe
import { Result } from '@praha/byethrow';
const fn = Result.fn({
safe: true,
try: async () => await Promise.resolve('ok'),
});
const result = await fn(); // Result.ResultAsync<string, never>
@categoryCreators fn ({
try: (input: string) => number try : (input: string input : string) => function parseInt(string: string, radix?: number): numberConverts a string to an integer.
@paramstring A string to convert into a number.@paramradix A value between 2 and 36 that specifies the base of the number in string.
If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
All other strings are considered decimal. parseInt (input: string input , 10),
catch: (error: unknown) => Error catch : () => new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error ('Invalid number')
});
// In this case, only the minimal code required for parseNumber
// is included in the bundle, while other features (andThen, pipe, etc.) are excluded#Best Practices
#Choosing Between Import Methods
- Use
Resultif you prefer explicit and descriptive naming that clearly indicates the purpose of each operation - Use
Rif you prefer fewer keystrokes and more concise code for faster development
#Important: Avoid Mixing Import Methods
We strongly recommend against mixing Result and R within the same codebase.
Choose one approach and stick with it consistently throughout your project to maintain code readability and consistency.
// @filename: mixed-imports.ts
// ❌ Don't mix approaches - this creates inconsistent code
import { import Result Result , import R R } from '@praha/byethrow';
const const validateId: (id: string) => Result.Result<string, never> validateId = (id: string id : string) => {
return import Result Result .const succeed: <string>(value: string) => Result.Result<string, never> (+1 overload) succeed (id: string id ); // Using Result
};
const const processData: Result.Result<string, never> processData = import R R .const pipe: <Result.Result<"data", never>, Result.Result<string, never>>(a: Result.Result<"data", never>, ab: (a: Result.Result<"data", never>) => Result.Result<string, never>) => Result.Result<string, never> (+25 overloads) pipe ( // Using R
import R R .const succeed: <"data">(value: "data") => Result.Result<"data", never> (+1 overload) succeed ('data'),
import R R .const andThen: <Result.Result<"data", never>, Result.Result<string, never>>(fn: (a: "data") => Result.Result<string, never>) => (result: Result.Result<"data", never>) => Result.Result<string, never> (+1 overload) andThen (const validateId: (id: string) => Result.Result<string, never> validateId )
);
// @filename: consistent-imports.ts
// ✅ Choose one approach and use it consistently
import { import Result Result } from '@praha/byethrow';
const const validateId: (id: string) => Result.Result<string, never> validateId = (id: string id : string) => {
return import Result Result .const succeed: <string>(value: string) => Result.Result<string, never> (+1 overload) succeed (id: string id );
};
const const processData: Result.Result<string, never> processData = import Result Result .const pipe: <Result.Result<"data", never>, Result.Result<string, never>>(a: Result.Result<"data", never>, ab: (a: Result.Result<"data", never>) => Result.Result<string, never>) => Result.Result<string, never> (+25 overloads) pipe (
import Result Result .const succeed: <"data">(value: "data") => Result.Result<"data", never> (+1 overload) succeed ('data'),
import Result Result .const andThen: <Result.Result<"data", never>, Result.Result<string, never>>(fn: (a: "data") => Result.Result<string, never>) => (result: Result.Result<"data", never>) => Result.Result<string, never> (+1 overload) andThen (const validateId: (id: string) => Result.Result<string, never> validateId )
);