#Mapping Results
The map and mapError functions allow you to transform the values inside a Result without changing its type (Success or Failure).
#map - Transforming Success Values
The map function transforms the success value of a Result. If the Result is a Failure, it passes through unchanged.
#Basic Usage
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<number, never> result = import Result Result .const pipe: <Result.Result<2, never>, Result.Result<number, never>>(a: Result.Result<2, never>, ab: (a: Result.Result<2, never>) => Result.Result<number, never>) => Result.Result<number, never> (+25 overloads) pipe (
import Result Result .const succeed: <2>(value: 2) => Result.Result<2, never> (+1 overload) succeed (2),
import Result Result .const map: <Result.Result<2, never>, number>(fn: (a: 2) => number) => (result: Result.Result<2, never>) => Result.Result<number, never> (+1 overload) map ((value: 2 value ) => value: 2 value * 10),
);
// { type: 'Success', value: 20 }#When the Result is a Failure
If the input is a Failure, map does nothing and returns the Failure as-is:
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<number, "error"> result = import Result Result .const pipe: <Result.Result<never, "error">, Result.Result<number, "error">>(a: Result.Result<never, "error">, ab: (a: Result.Result<never, "error">) => Result.Result<number, "error">) => Result.Result<number, "error"> (+25 overloads) pipe (
import Result Result .const fail: <"error">(error: "error") => Result.Result<never, "error"> (+1 overload) fail ('error'),
import Result Result .const map: <Result.Result<never, "error">, number>(fn: (a: never) => number) => (result: Result.Result<never, "error">) => Result.Result<number, "error"> (+1 overload) map ((value: never value ) => value: never value * 10),
);
// { type: 'Failure', error: 'error' }#Example: Formatting Data for Display
A common use case is transforming raw data into a display-friendly format:
import { import Result Result } from '@praha/byethrow';
type type ProductRow = {
id: number;
name: string;
priceInCents: number;
}
ProductRow = {
id: number id : number;
name: string name : string;
priceInCents: number priceInCents : number;
};
type type ProductView = {
id: string;
name: string;
price: string;
}
ProductView = {
id: string id : string;
name: string name : string;
price: string price : string;
};
const const formatPrice: (cents: number) => string formatPrice = (cents: number cents : number): string => `$${(cents: number cents / 100).Number.toFixed(fractionDigits?: number): stringReturns a string representing a number in fixed-point notation.
@paramfractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive. toFixed (2)}`;
const const toProductView: (product: ProductRow) => ProductView toProductView = (product: ProductRow product : type ProductRow = {
id: number;
name: string;
priceInCents: number;
}
ProductRow ): type ProductView = {
id: string;
name: string;
price: string;
}
ProductView => ({
id: string id : var String: StringConstructor
(value?: any) => string
Allows manipulation and formatting of text strings and determination and location of substrings within strings.
String (product: ProductRow product .id: number id ),
name: string name : product: ProductRow product .name: string name ,
price: string price : const formatPrice: (cents: number) => string formatPrice (product: ProductRow product .priceInCents: number priceInCents ),
});
const const getProductForDisplay: (id: number) => Result.ResultAsync<ProductView, "NotFound"> getProductForDisplay = (id: number id : number): import Result Result .type ResultAsync<T, E> = Promise<Result.Result<T, E>>An asynchronous variant of
Result
, wrapped in a Promise.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const fetchData = async (): Result.ResultAsync<string, Error> => {
try {
const data = await fetch('...');
return { type: 'Success', value: await data.text() };
} catch (err) {
return { type: 'Failure', error: err as Error };
}
};
@categoryCore Types ResultAsync <type ProductView = {
id: string;
name: string;
price: string;
}
ProductView , 'NotFound'> => {
return import Result Result .const pipe: <Result.ResultAsync<ProductRow, "NotFound">, Result.ResultAsync<ProductView, "NotFound">>(a: Result.ResultAsync<ProductRow, "NotFound">, ab: (a: Result.ResultAsync<ProductRow, "NotFound">) => Result.ResultAsync<ProductView, "NotFound">) => Result.ResultAsync<ProductView, "NotFound"> (+25 overloads) pipe (
const fetchProduct: (id: number) => Result.ResultAsync<ProductRow, "NotFound"> fetchProduct (id: number id ),
import Result Result .const map: <Result.ResultAsync<ProductRow, "NotFound">, ProductView>(fn: (a: ProductRow) => ProductView) => (result: Result.ResultAsync<ProductRow, "NotFound">) => Result.ResultAsync<ProductView, "NotFound"> (+1 overload) map (const toProductView: (product: ProductRow) => ProductView toProductView ),
);
};
// Transforms the raw product data into a UI-friendly format#mapError - Transforming Error Values
The mapError function transforms the error value of a Result. If the Result is a Success, it passes through unchanged.
#Basic Usage
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<never, Error> result = import Result Result .const pipe: <Result.Result<never, "NotFound">, Result.Result<never, Error>>(a: Result.Result<never, "NotFound">, ab: (a: Result.Result<never, "NotFound">) => Result.Result<never, Error>) => Result.Result<never, Error> (+25 overloads) pipe (
import Result Result .const fail: <"NotFound">(error: "NotFound") => Result.Result<never, "NotFound"> (+1 overload) fail ('NotFound'),
import Result Result .const mapError: <Result.Result<never, "NotFound">, Error>(fn: (a: "NotFound") => Error) => (result: Result.Result<never, "NotFound">) => Result.Result<never, Error> (+1 overload) mapError ((error: "NotFound" error ) => new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error (error: "NotFound" error )),
);
// { type: 'Failure', error: Error('NotFound') }#When the Result is a Success
If the input is a Success, mapError does nothing and returns the Success as-is:
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<123, Error> result = import Result Result .const pipe: <Result.Result<123, never>, Result.Result<123, Error>>(a: Result.Result<123, never>, ab: (a: Result.Result<123, never>) => Result.Result<123, Error>) => Result.Result<123, Error> (+25 overloads) pipe (
import Result Result .const succeed: <123>(value: 123) => Result.Result<123, never> (+1 overload) succeed (123),
import Result Result .const mapError: <Result.Result<123, never>, Error>(fn: (a: never) => Error) => (result: Result.Result<123, never>) => Result.Result<123, Error> (+1 overload) mapError ((error: never error ) => new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error (error: never error )),
);
// { type: 'Success', value: 123 }#Example: Converting Internal Errors to HTTP Responses
A common use case is converting domain-specific errors into standardized HTTP error responses:
import { import Result Result } from '@praha/byethrow';
type type DomainError = {
type: "NotFound";
resource: string;
} | {
type: "ValidationError";
field: string;
message: string;
} | {
type: "Unauthorized";
}
DomainError =
| { type: "NotFound" type : 'NotFound'; resource: string resource : string }
| { type: "ValidationError" type : 'ValidationError'; field: string field : string; message: string message : string }
| { type: "Unauthorized" type : 'Unauthorized' };
type type HttpError = {
status: number;
body: {
error: string;
details?: string;
};
}
HttpError = {
status: number status : number;
body: {
error: string;
details?: string;
}
body : { error: string error : string; details?: string | undefined details ?: string };
};
const const toHttpError: (error: DomainError) => HttpError toHttpError = (error: DomainError error : type DomainError = {
type: "NotFound";
resource: string;
} | {
type: "ValidationError";
field: string;
message: string;
} | {
type: "Unauthorized";
}
DomainError ): type HttpError = {
status: number;
body: {
error: string;
details?: string;
};
}
HttpError => {
switch (error: DomainError error .type: "NotFound" | "ValidationError" | "Unauthorized" type ) {
case 'NotFound':
return {
status: number status : 404,
body: {
error: string;
details?: string;
}
body : { error: string error : 'Not Found', details?: string | undefined details : `${error: {
type: "NotFound";
resource: string;
}
error .resource: string resource } was not found` },
};
case 'ValidationError':
return {
status: number status : 400,
body: {
error: string;
details?: string;
}
body : { error: string error : 'Bad Request', details?: string | undefined details : `${error: {
type: "ValidationError";
field: string;
message: string;
}
error .field: string field }: ${error: {
type: "ValidationError";
field: string;
message: string;
}
error .message: string message }` },
};
case 'Unauthorized':
return {
status: number status : 401,
body: {
error: string;
details?: string;
}
body : { error: string error : 'Unauthorized' },
};
}
};
const const handleRequest: () => Result.ResultAsync<{
data: string;
}, HttpError>
handleRequest = (): import Result Result .type ResultAsync<T, E> = Promise<Result.Result<T, E>>An asynchronous variant of
Result
, wrapped in a Promise.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const fetchData = async (): Result.ResultAsync<string, Error> => {
try {
const data = await fetch('...');
return { type: 'Success', value: await data.text() };
} catch (err) {
return { type: 'Failure', error: err as Error };
}
};
@categoryCore Types ResultAsync <{ data: string data : string }, type HttpError = {
status: number;
body: {
error: string;
details?: string;
};
}
HttpError > => {
return import Result Result .const pipe: <Result.ResultAsync<{
data: string;
}, DomainError>, Result.ResultAsync<{
data: string;
}, HttpError>>(a: Result.ResultAsync<{
data: string;
}, DomainError>, ab: (a: Result.ResultAsync<{
data: string;
}, DomainError>) => Result.ResultAsync<{
data: string;
}, HttpError>) => Result.ResultAsync<{
data: string;
}, HttpError> (+25 overloads)
pipe (
const processRequest: () => Result.ResultAsync<{
data: string;
}, DomainError>
processRequest (),
import Result Result .const mapError: <Result.ResultAsync<{
data: string;
}, DomainError>, HttpError>(fn: (a: DomainError) => HttpError) => (result: Result.ResultAsync<{
data: string;
}, DomainError>) => Result.ResultAsync<{
data: string;
}, HttpError> (+1 overload)
mapError (const toHttpError: (error: DomainError) => HttpError toHttpError ),
);
};
// Converts domain errors into HTTP-appropriate error responses#Combining map and mapError
You can use both map and mapError in the same pipeline to transform both success and error values.
Suppose you have a function that fetches a user from the database. The raw data may need formatting for the UI, and internal error codes need to be converted into user-friendly messages:
import { import Result Result } from '@praha/byethrow';
const const toView: (row: UserRow) => UserView toView = (row: UserRow row : type UserRow = {
id: number;
first_name: string;
last_name: string;
created_at: string;
}
UserRow ): type UserView = {
id: string;
fullName: string;
memberSince: string;
}
UserView => ({
id: string id : var String: StringConstructor
(value?: any) => string
Allows manipulation and formatting of text strings and determination and location of substrings within strings.
String (row: UserRow row .id: number id ),
fullName: string fullName : `${row: UserRow row .first_name: string first_name } ${row: UserRow row .last_name: string last_name }`,
memberSince: string memberSince : new var Date: DateConstructor
new (value: number | string | Date) => Date (+4 overloads)
Date (row: UserRow row .created_at: string created_at ).Date.toLocaleDateString(locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions): string (+2 overloads)Converts a date to a string by using the current or specified locale.
@paramlocales A locale string, array of locale strings, Intl.Locale object, or array of Intl.Locale objects that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.@paramoptions An object that contains one or more properties that specify comparison options. toLocaleDateString (),
});
const const toHttpError: (error: DatabaseError) => HttpError toHttpError = (error: DatabaseError error : type DatabaseError = "RECORD_NOT_FOUND" | "CONNECTION_FAILED" | "TIMEOUT" DatabaseError ): type HttpError = {
status: number;
body: {
error: string;
details?: string;
};
}
HttpError => {
switch (error: DatabaseError error ) {
case 'RECORD_NOT_FOUND':
return {
status: number status : 404,
body: {
error: string;
details?: string;
}
body : {
error: string error : 'User Not Found',
details?: string | undefined details : 'The requested user does not exist.',
},
};
case 'CONNECTION_FAILED':
case 'TIMEOUT':
return {
status: number status : 503,
body: {
error: string;
details?: string;
}
body : {
error: string error : 'Service Unavailable',
details?: string | undefined details : 'Please try again later.',
},
};
}
};
const const getUserForView: (id: number) => Result.ResultAsync<UserView, HttpError> getUserForView = (id: number id : number): import Result Result .type ResultAsync<T, E> = Promise<Result.Result<T, E>>An asynchronous variant of
Result
, wrapped in a Promise.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const fetchData = async (): Result.ResultAsync<string, Error> => {
try {
const data = await fetch('...');
return { type: 'Success', value: await data.text() };
} catch (err) {
return { type: 'Failure', error: err as Error };
}
};
@categoryCore Types ResultAsync <type UserView = {
id: string;
fullName: string;
memberSince: string;
}
UserView , type HttpError = {
status: number;
body: {
error: string;
details?: string;
};
}
HttpError > => {
return import Result Result .const pipe: <Result.ResultAsync<UserRow, DatabaseError>, Result.ResultAsync<UserView, DatabaseError>, Result.ResultAsync<UserView, HttpError>>(a: Result.ResultAsync<UserRow, DatabaseError>, ab: (a: Result.ResultAsync<UserRow, DatabaseError>) => Result.ResultAsync<UserView, DatabaseError>, bc: (b: Result.ResultAsync<UserView, DatabaseError>) => Result.ResultAsync<...>) => Result.ResultAsync<...> (+25 overloads) pipe (
const fetchUser: (id: number) => Result.ResultAsync<UserRow, DatabaseError> fetchUser (id: number id ),
import Result Result .const map: <Result.ResultAsync<UserRow, DatabaseError>, UserView>(fn: (a: UserRow) => UserView) => (result: Result.ResultAsync<UserRow, DatabaseError>) => Result.ResultAsync<UserView, DatabaseError> (+1 overload) map (const toView: (row: UserRow) => UserView toView ),
import Result Result .const mapError: <Result.ResultAsync<UserView, DatabaseError>, HttpError>(fn: (a: DatabaseError) => HttpError) => (result: Result.ResultAsync<UserView, DatabaseError>) => Result.ResultAsync<UserView, HttpError> (+1 overload) mapError (const toHttpError: (error: DatabaseError) => HttpError toHttpError ),
);
}#References
| Function | Purpose |
|---|---|
| map(fn) | Transform the success value of a Result |
| mapError(fn) | Transform the error value of a Result |
