0605
LOGS
typescript のエラーハンドリング
Either
型やResult
型は非常に強力なパターン- 関数の成功結果と失敗結果を明確に型で表現し、より安全で堅牢なコードを書くのに役立つらしい
- 従来のエラーハンドリングでは、以下のような方法
null
やundefined
を返す- 成功時はそもそもの値を返す
- ただ,この2つは呼び出し側がしっかりチェックしないと,null なのか undefined なのかわかりにくい
- 例外をスローする
throw new Error(...)
- 関数のシグネチャを見ただけでは例外をスローする可能性がわからない
try-catch
での処理を忘れるとプログラムがクラッシュする可能性がある- どの関数がどの例外を投げるか把握しにくい
- これらの問題を解決するために「エラーも正常な値の一種として返す」というアプローチを取る
Result
型(Result<T, E>
)- 操作が成功した場合には
Ok<T>
型の値を,失敗した場合にはErr<E>
型の値を保持 - T は成功時の値の型,E は失敗時のエラー情報の型
- Result 型の定義例
// 成功時の値をラップする型 type Ok<T> = { readonly _tag: 'Ok'; readonly value: T; }; // エラー情報をラップする型 type Err<E> = { readonly _tag: 'Err'; readonly error: E; }; // Result型本体 type Result<T, E> = Ok<T> | Err<E>; // ヘルパー関数 (Result型の値を簡単に作成するため) const ok = <T, E = never>(value: T): Result<T, E> => ({ _tag: 'Ok', value }); const err = <E, T = never>(error: E): Result<T, E> => ({ _tag: 'Err', error }); // 型ガード (Result型がOkかErrかを判別するため) const isOk = <T, E>(result: Result<T, E>): result is Ok<T> => result._tag === 'Ok'; const isErr = <T, E>(result: Result<T, E>): result is Err<E> => result._tag === 'Err';
- 使用例(文字列を数値にパースする関数)
function parseNumberResult(input: string): Result<number, string> { const num = Number(input); if (isNaN(num)) { return err(`'${input}' is not a valid number.`); // Err<string> を返す } return ok(num); // Ok<number> を返す } // 使用例 const result1 = parseNumberResult("123"); if (isOk(result1)) { // ここでは result1 は Ok<number> 型として扱える console.log(`Success (Result): Value is ${result1.value}, Doubled: ${result1.value * 2}`); } else { // ここでは result1 は Err<string> 型として扱える console.error(`Error (Result): ${result1.error}`); } const result2 = parseNumberResult("abc"); if (isOk(result2)) { console.log(`Success (Result): Value is ${result2.value}`); } else { console.error(`Error (Result): ${result2.error}`); } // 出力例: // Success (Result): Value is 123, Doubled: 246 // Error (Result): 'abc' is not a valid number.
is
ってなんだ(以前も調べたし,おそらく開発日誌にも書いたことある気がする- 型推論を補強する
user-defined type guard(ユーザー定義型ガード)
に使うらしい unknown
,any
,Union
型を絞れる例
const isString = (test: unknown): boolean => { return typeof test === "string"; }; const example = (foo: unknown) => { if (isString(foo)) { console.log(foo.length); // Error fooはまだunknownとして推論される } };
- こんな感じ
- これをこうする
- const isString = (test: unknown): boolean => { + const isString = (test: unknown): test is string => { return typeof test === "string"; };
type predicate
というらしい- ただし,
return typeof test === "number";
にしても,型エラーは出ないが,実行時エラーになる - 有名な問題っぽい
- ライブラリで対策した事例 もある
- ただし,
- こんな感じ
- 型推論を補強する
- 操作が成功した場合には
Either
型(Either<L, R>
)- 2つの可能性のうちどちらか一方の値を保持する
- 慣習的に
Left<L>
(左側)をエラーや失敗ケースに,Right<R>
(右側)を正常系や成功ケースに使う - Either 型の定義例
```ts
// Left型 (慣習的にエラー用)
type Left
= { readonly _tag: 'Left'; readonly left: L; };
// Right型 (慣習的に成功用) type Right
= { readonly _tag: 'Right'; readonly right: R; }; // Either型本体 type Either
= Left | Right ; // ヘルパー関数 const left =
(value: L): Either => ({ _tag: 'Left', left: value }); const right = (value: R): Either => ({ _tag: 'Right', right: value }); // 型ガード const isLeft =
(either: Either ): either is Left => either._tag === 'Left'; const isRight = (either: Either ): either is Right => either._tag === 'Right'; ``` - Branded Type
その前に,
判別可能な Union 型(Discriminated Union)
以下サンプルコード
type Success = { type: 'success'; data: string; }; type Failure = { type: 'failure'; error: string; }; type Result = Success | Failure; function handleResult(result: Result) { switch (result.type) { case 'success': // resultはSuccess型に絞り込まれる console.log('Data:', result.data); break; case 'failure': // resultはFailure型に絞り込まれる console.error('Error:', result.error); break; } }
- 共通フィールドがあるときに使えるやつ
公称型(Nominal Typing)
いいなぁ.Golang はこれだったのか- TypeScript は構造的部分型なので
- 柔軟性は確かに高いが,公称型がしたいときもある
- → やっと登場
Branded Type
- サンプルコード
type Brand<T, B> = T & { __brand: B }; type UserId = Brand<string, 'UserId'>; type Email = Brand<string, 'Email'>; // 使ってみる const sendEmail = (email: Email) => { console.log("Sending email to:", email); }; const userId = "user123" as UserId; const email = "user@example.com" as Email; sendEmail(email); // OK sendEmail(userId); // type error! // Argument of type 'UserId' is not assignable to parameter of type 'Email'. // 通常のstringも受け付けない const plainString = "plain@example.com"; sendEmail(plainString); // type error! // Argument of type 'string' is not assignable to parameter of type 'Email'.
AIとAWS まわり
- 生成AIアプリやモデルのバックエンドシステムへのアクセスは 最小権限の原則 に従い,モデルの動作に必要な最小レベルのアクセスのみに制限
- 特権的な操作を実行する場合にはユーザの承認など 人による判断を入れる(Human in the loop)
- もちろん限界はある
- 外部コンテンツをユーザープロンプトから分離
- 生成AIアプリやモデルを信頼されないユーザーとして扱い,外部ソース,拡張可能な機能(プラグインやダウンストリーム機能など)間の信頼境界を確立する
ダウンストリーム機能
主にIT業界やネットワークの文脈で使われる言葉で、サーバーや中心側からクライアントや末端側へ向かうデータや信号の流れ、またはその速度、方向を指します。具体的には、Webサーバーからブラウザへデータを送る際など、下り方向の通信を指します。 by Gemini -