#Result を変換する
map と mapError 関数を使うと、Result の型(Success または Failure)を変えずに、内部の値を変換できます。
#map - 成功値を変換する
map 関数は Result の成功値を変換します。
Result が Failure の場合は、変更されずにそのまま通過します。
#基本的な使い方
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 }#Result が Failure の場合
入力が 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): 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 ),
);
};
// 生の商品データを UI に適した形式に変換#mapError - 失敗値を変換する
mapError 関数は Result の失敗値を変換します。
Result が Success の場合は、変更されずにそのまま通過します。
#基本的な使い方
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.@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 ),
);
};
// ドメインエラーを HTTP 適切なエラーレスポンスに変換#map と mapError の組み合わせ
同じパイプラインで map と mapError の両方を使って、成功値と失敗値の両方を変換できます。
データベースからユーザーを取得する関数があるとします。 生データは 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.@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 ),
);
}#リファレンス
| 関数 | 目的 |
|---|---|
| map(fn) | Result の成功値を変換する |
| mapError(fn) | Result のエラー値を変換する |
