TypeScript async/await で Result 型みたいなことをやりたい
ふとしです。
Result っていうか (async/await で得られる値を使う部分で) try catch 以外でどうにかしたいという話です。
try catch 内でスコープが切られているのがつらい
async/await を使うにあたって try catch を使いますが、内部で定義した変数を持ち出すことができません。
async function run() {
try {
const a = await fetchA();
} catch(e) {
console.error(e);
}
const b = await fetchB(a); // ダメ
}
しかも e の型を確実にする方法がありません。
そこで正否を判定できる Result 型を返して、try catch 不要かつエラーの型もわかる状態にしたいと思いました。
やる
配列で返す Go スタイル もありましたが value 側にタイプガードが効かず !
を使う感じだったのでやめて、以下の通り。
const OkMarker = "OkMarker";
const NgMarker = "NgMarker";
export type Ok<T> = { value: T; _: typeof OkMarker };
export type Err<E> = { value: E; _: typeof NgMarker };
export type Result<T, E> = Ok<T> | Err<E>;
export function toOk<T>(value: T): Ok<T> {
return { value, _: OkMarker };
}
export function toErr<E>(value: E): Err<E> {
return { value, _: NgMarker };
}
export function isOk<T>(a: any): a is Ok<T> {
return a._ === OkMarker;
}
あとは try catch を局地に閉じ込めて (ラップする関数書けそう)
async function fetchNew(): Promise<Result<string, Error>> {
try {
return toOk(await fetchA());
} catch(e) {
return toErr(e);
}
}
救済措置とかの関数を書いて
function doSomething(e: Error) :string {
return "retried";
}
本処理に。
async function run() {
const re = await fetchNew();
const a = isOk(re)
? re.value
: doSomething(re.value);
const b = await fetchB(a);
}
まとめ
try catch 別に嫌いではないんですがスコープが切れるのがどうも取り回ししづらく、なんとかならんかなという施策でした。