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
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Kodak
February 05, 2023
Technology
5.5k
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
新しい SLO が良い感じにハマっている話
z63d
5
2.1k
ブラウザ研修 2026
recruitengineers
PRO
4
610
モノリス Rails でも日中に rails db:migrate を走らせたい! / Daytime rails db:migrate on Monolithic Rails!
euglena1215
4
510
変化の早いClaude Codeを 書籍に落とし込む
oikon48
7
1.3k
個人OSSが、机の上から世界に広がるまでの話
shinyasaita
1
350
社内の7割が使うデータ基盤を、 データチーム2人で回すためにやったこと
koh_yoshi
4
520
NetBoxを利用した作業効率化の試み_NetDevNight4
tnoha
0
470
【CEDEC2026】コードレビュー支援ツール開発から学ぶ:LLMを用いた業務システムの実践的な運用設計と誤出力対策
cygames
PRO
0
580
事業価値と Engineering 2026年度版
recruitengineers
PRO
41
20k
Service Connect 上のサービスに ECS Service の外側から到達できなかった話
ota1022
1
130
『三匹の子ぶた』から学ぶネットワークセキュリティの昔と今 / Network Security: Then and Now Through the Lens of The Three Little Pigs
nttcom
1
1.7k
ボトムアップ文化が強い組織で セキュリティをどう根付かせていくかの現在進行形の話 / Making Security Stick in a Bottom-Up Organization
yamaguchitk333
0
160
Featured
See All Featured
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
610
Google's AI Overviews - The New Search
badams
0
1.1k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.2k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
ラッコキーワード サービス紹介資料
rakko
1
4.2M
Color Theory Basics | Prateek | Gurzu
gurzu
0
410
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
Evolving SEO for Evolving Search Engines
ryanjones
0
250
From π to Pie charts
rasagy
0
250
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を(可能な限り)用いないので、以下の恩恵が得られる。 - エラーハンドリングが容易。 - 型安全。 -
エラーのテストが容易。 是非、使ってみてください!!!!