Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
TypeScriptの「Result型」のすゝめ
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Kodak
February 05, 2023
Technology
5.4k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
TypeScriptの「Result型」のすゝめ
Kodak
February 05, 2023
Other Decks in Technology
See All in Technology
AI Adaptable なテストを整える工夫 / Ways to Make Your Tests AI-Adaptable
bitkey
PRO
3
230
protovalidate-es を導入してみた
bengo4com
0
160
製造業のクラウド活用最適解〜AI,DXを加速するデータ基盤の作り方〜
hamadakoji
0
420
Mastering Ruby Box
tagomoris
3
150
自律型AIエージェントは何を破壊するのか
kojira
0
130
Microsoft Build Keynoteふりかえり
tomokusaba
0
110
Building applications in the Gemini API family.
line_developers_tw
PRO
0
2.3k
生成 AI × MCP で切り拓く次世代 SRE!自律型運用への挑戦と開発者体験の進化
_awache
0
170
2026.06.13_AI時代に事業会社が「SIer出身エンジニア」を求める理由 / Why Businesses Seek Engineers with a System Integrator Background in the AI Era
jumtech
0
950
機械学習を「社会実装」するということ 2026年夏版 / Social Implementation of Machine Learning June 2026 Version
moepy_stats
2
440
Reliability in the Age of AI: Engineering for AI Velocity
rrreeeyyy
0
110
Claude Code の Sandbox 機能を Anthropic Sandbox Runtime(srt) で試そう!/lets-play-anthropic-sandbox-runtime
tomoki10
1
260
Featured
See All Featured
A Soul's Torment
seathinner
6
2.9k
Speed Design
sergeychernyshev
33
1.8k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
220
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.6k
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
Utilizing Notion as your number one productivity tool
mfonobong
4
320
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
200
SEO for Brand Visibility & Recognition
aleyda
0
4.6k
Tell your own story through comics
letsgokoyo
1
950
ラッコキーワード サービス紹介資料
rakko
1
3.6M
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
160
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
1.1k
Transcript
「Result型」のすゝめ 第20回「技術共有会」
今回お話する内容 リンク https://qiita.com/Kodak_tmo/items/ d48eb3497be18896b999
「Result型」とは? 何かしら処理の成功、または失敗を表す型のこと。 Swift、Scala、最近流行りのRust等には言語自体に「Result型」を定義できる仕組みがある。 残念ながら、TypeScriptには、そのような仕組みが無いため、自分で実装する必要がある。 Result型いいなぁ.... Result型あるー Result型あるー Result型あるー ....
「Result型」を見てみよう type Result<T, E extends Error> = Success<T> | Failure<E>;
class Success<T> { readonly value: T; constructor(value: T) { this.value = value; } isSuccess(): this is Success<T> { return true; } isFailure(): this is Failure<Error> { return false; } } class Failure<E extends Error> { readonly error: E; constructor(error: E) { this.error = error; } isSuccess(): this is Success<unknown> { return false; } isFailure(): this is Failure<E> { return true; } }
「Result型」を見てみよう type Result<T, E extends Error> = Success<T> | Failure<E>;
class Success<T> { readonly value: T; constructor(value: T) { this.value = value; } isSuccess(): this is Success<T> { return true; } isFailure(): this is Failure<Error> { return false; } } class Failure<E extends Error> { readonly error: E; constructor(error: E) { this.error = error; } isSuccess(): this is Success<unknown> { return false; } isFailure(): this is Failure<E> { return true; } } 成功クラス 失敗クラス
「Result型」を見てみよう type Result<T, E extends Error> = Success<T> | Failure<E>;
class Success<T> { readonly value: T; constructor(value: T) { this.value = value; } isSuccess(): this is Success<T> { return true; } isFailure(): this is Failure<Error> { return false; } } class Failure<E extends Error> { readonly error: E; constructor(error: E) { this.error = error; } isSuccess(): this is Success<unknown> { return false; } isFailure(): this is Failure<E> { return true; } } コンストラクターで成功の 結果を受け取る。 インスタンス変数から結果 を参照できるようにする。 コンストラクターで失敗の 結果を受け取る。 インスタンス変数から結果 を参照できるようにする。
「Result型」を見てみよう type Result<T, E extends Error> = Success<T> | Failure<E>;
class Success<T> { readonly value: T; constructor(value: T) { this.value = value; } isSuccess(): this is Success<T> { return true; } isFailure(): this is Failure<Error> { return false; } } class Failure<E extends Error> { readonly error: E; constructor(error: E) { this.error = error; } isSuccess(): this is Success<unknown> { return false; } isFailure(): this is Failure<E> { return true; } } 成功 or 失敗の判定メソッ ドを用意する。 成功 or 失敗の判定メソッ ドを用意する。
「Result型」を見てみよう type Result<T, E extends Error> = Success<T> | Failure<E>;
class Success<T> { readonly value: T; constructor(value: T) { this.value = value; } isSuccess(): this is Success<T> { return true; } isFailure(): this is Failure<Error> { return false; } } class Failure<E extends Error> { readonly error: E; constructor(error: E) { this.error = error; } isSuccess(): this is Success<unknown> { return false; } isFailure(): this is Failure<E> { return true; } } ※実は、各クラスの反対のメソッド(成功なら失 敗を判定するメソッド)は使用していない。 しかし、このメソッドを実装しておかないと、 コード補完が上手く働かない。 ※実は、各クラスの反対のメソッド(成功なら失敗を判定 するメソッド)は使用していない。 しかし、このメソッドを実装しておかないと、コード補完が 上手く働かない。 仕組みは以下と同じ。TypeScriptは「構造的部分型」を 採用しているのでこのような結果になる。
「Result型」を見てみよう type Result<T, E extends Error> = Success<T> | Failure<E>;
class Success<T> { readonly value: T; constructor(value: T) { this.value = value; } isSuccess(): this is Success<T> { return true; } isFailure(): this is Failure<Error> { return false; } } class Failure<E extends Error> { readonly error: E; constructor(error: E) { this.error = error; } isSuccess(): this is Success<unknown> { return false; } isFailure(): this is Failure<E> { return true; } } 最後に、Result型を定義する。 Success型とFailure型をUnion型にする。 何かしら処理の成功、または失敗を表す型と なる。
「Result型」の使い方(その1)
「Result型」の使い方(その2)
「Result型」の特徴・まとめ - 「Result型」は、エラーハンドリング手法の1つ。 - try-catchを(可能な限り)用いないので、以下の恩恵が得られる。 - エラーハンドリングが容易。 - 型安全。 -
エラーのテストが容易。
エラーハンドリングが容易とは?(その1) // こんなことになったことはない? ライブラリ、 API、JSON parse するたびに try-catch して、エラーハンドリングが複雑化。。。 type
JsonType = { msg: string } function fooChildren <T>(str: string) { try { return JSON.parse(str) as T } catch(error) { // ...省略 } } function foo<T>(str: string) { try { const json1 = fooChildren <T>(str) const json2 = fooChildren <T>(str) return { json1, json2 } } catch(error) { // ...省略(json1でエラーが出た場合、 json2でエラーが出た場合でエラーの出し分け、どうコントロールするかを考える必要がある) } } function main() { try { return foo<JsonType >('{ "msg":"hello" }' ) // {“json1”:{“msg”:”hello”}, “json2”:{“msg”:”hello”}} } catch(error) { // ...省略(json1でエラーが出た場合、 json2でエラーが出た場合でエラーの出し分け、どうコントロールするかを考える必要がある) } } ・エラーハンドリング箇所が多い ・考えることが多い ・コードが読み辛い
エラーハンドリングが容易とは?(その2) // こんなことになったことはない? ライブラリ、 API、JSON parse するたびにtry-catchして、エラーハンドリングが複雑化。。。 type JsonType= {
msg: string } function fooChildren <T>(str: string) { try { return JSON.parse(str) as T } catch(error) { // ...省略 } } function foo<T>(str: string) { const json1 = fooChildren <T>(str) const json2 = fooChildren <T>(str) return { json1, json2 } } function main() { try { return foo<JsonType>('{ "msg":"hello" }' ) // {“json1”:{“msg”:”hello”}, “json2”:{“msg”:”hello”}} } catch(error) { if (error instanceof Error) { // エラー判別処理が巨大化・複雑化 if (error.name === 'xxxx') { // ...省略 } if (error.name === 'xxxx') { // ...省略 } } throw new Error('unknown Error' ) } } ・エラー判定処理が巨大化・複雑化
「Result型」を使うと?
エラーハンドリングが容易とは?(Result型) type JsonType= { msg: string } function fooChildren <T>(str:
string): Result<T, Error> { try { const json = JSON.parse(str) as T return new Success(json) } catch(error) { // ...省略 return new Failure(new Error('unknown Error' )) } } function foo<T>(str: string): Result<{json1: T,json2: T }, Error> { const result1 = fooChildren <T>(str) if (result1.isFailure()) { // joson1のエラー return new Failure(result1.error) } const result2 = fooChildren <T>(str) if (result2.isFailure()) { // joson2のエラー return new Failure(result2.error) } return new Success({json1:result1.value, json2: result2.value}) } function main() { const result = foo<JsonType>('{ "msg":"hello" }' ) // {“json1”:{“msg”:”hello”}, “json2”:{“msg”:”hello”}} if(result.isFailure()) { // ...省略(そのまま出力で OK。ここでごちゃごちゃしない) return } return result.value } ・エラー判定処理がif文の下にすぐ書ける ・1つ1つのエラーを個別に考えることができる ・コードが見易い
型安全とは? type JsonType = { msg: string } function fooChildren
<T>(str: string) { try { return JSON.parse(str) as T } catch(error) { // ...省略 } } function foo<T>(str: string) { // foo() の戻り値の型も {json1:T, json2:T} となり、catchが考慮されない。。。 try { const json1 = fooChildren <T>(str) // json1 の型、fooChildren() の戻り値の型は T。つまり、 catchされたエラーが考慮されていない。。。 const json2 = fooChildren <T>(str) // json2 の型、fooChildren() の戻り値の型は T。つまり、 catchされたエラーが考慮されていない。。。 return { json1, json2 } } catch(error) { // ...省略 } } function main() { try { return foo<JsonType >('{ "msg":"hello" }' ) // {“json1”:{“msg”:”hello”}, “json2”:{“msg”:”hello”}} } catch(error) { // ...省略 } } ・catchされたエラーが型として表現されない。呼び出した関数 がエラーになるかどうかは、実際にコードを見なくてはならな い。「Result型」を使えば、このあたりは考慮しなくて良い。
エラーのテストが容易とは? テストにおいて、エラー用のマッチャー( toThrow()、toThrowError())を使用しなくてよくなり、 toBe() や toEqual() を使用 して評価することができる。 const result
= main() expect(result.isFailure () ? result.error.statusCode : undefined ).toBe(500) expect(result.isFailure () ? result.error.code : undefined ).toBe('APP_FOO_FUNCTION_ERROR' )
「Qiita」で頂いたコメントを見る
Qiitaでのコメント(その1) Go言語は、各プログラミング言語が抱える問題の 1つである言語自体の複雑化。それによる批判などを解決した上で、 各言語の良い部分を参考にして作られた比較的新しい言語です。 そうやって産まれた言語で、 try-catchが無い。。。というのは、考えさせられるものがありますね。 (ちなみにGo言語のエラーハンドリングは、以下のように `if` で行います。Result型に似てますよね? 2009年生まれだよ
Qiitaでのコメント(その2) 上のリンク先から例外箇所について私なりに纏め てみると、「例外は実質的に見えない goto文であり、目に見える goto文よりもわかり辛い。例外があること で、そのコードが正しい かどうかを判断するには、 どこか別なところ を見なければならず、 多くのことを頭の中で一時的に記憶しておく必要があり、大変であ
る。」という感じです。 例外は実質的に見えない goto文なんだよ。ということが全てを物語っている気がしますね。
「Result型」の特徴・まとめ(再) - 「Result型」は、エラーハンドリング手法の1つ。 - try-catchを(可能な限り)用いないので、以下の恩恵が得られる。 - エラーハンドリングが容易。 - 型安全。 -
エラーのテストが容易。 是非、使ってみてください!!!!