Result を変換する

mapmapError 関数を使うと、Result の型(Success または Failure)を変えずに、内部の値を変換できます。

map - 成功値を変換する

map 関数は Result の成功値を変換します。 ResultFailure の場合は、変更されずにそのまま通過します。

基本的な使い方

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 }

ResultFailure の場合

入力が Failure の場合、map は何もせず、Failure をそのまま返します。

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' }

例:表示用のデータフォーマット

よくある使い方として、生データを表示に適した形式に変換する例があります。

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
),
); }; // 生の商品データを UI に適した形式に変換

mapError - 失敗値を変換する

mapError 関数は Result の失敗値を変換します。 ResultSuccess の場合は、変更されずにそのまま通過します。

基本的な使い方

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') }

Result が Success の場合

入力が Success の場合、mapError は何もせず、Success をそのまま返します。

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 }

例:内部エラーを HTTP レスポンスに変換

よくある使い方として、ドメイン固有のエラーを標準化された HTTP エラーレスポンスに変換する例があります。

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
),
); }; // ドメインエラーを HTTP 適切なエラーレスポンスに変換

mapmapError の組み合わせ

同じパイプラインで mapmapError の両方を使って、成功値と失敗値の両方を変換できます。

データベースからユーザーを取得する関数があるとします。 生データは UI 用にフォーマットが必要で、内部エラーコードはユーザーフレンドリーなメッセージに変換する必要がある場合を考えます。

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
),
); }

リファレンス

関数目的
map(fn)Result の成功値を変換する
mapError(fn)Result のエラー値を変換する