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): string

Returns 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.@example
import { 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.@example
import { 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.@example
import { 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

FunctionPurpose
map(fn)Transform the success value of a Result
mapError(fn)Transform the error value of a Result