Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
TypeScriptの「Result型」のすゝめ
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
山手線を徒歩で一周してわかった、 位置情報アプリは「足」が最強のデバッガー
hinakko
0
140
ASTを使って影響範囲を特定する
nealle
0
180
Screen Lens - 今見てる画面を翻訳する
komagata
0
270
コーディングエージェントでM5Stack系の開発を少し試した時の話 / M5 Japan Tour 2026 Autumn 東京
you
PRO
0
190
DEFCON34-Write-up_HYCu-MYCu
daikiokazaki
0
160
range over func 2年間の軌跡 Issue #56413 はGoのエコシステムをどう変えたか
ryujicre8ive
0
130
NW運用でNWトポロジ可視化ツールに期待すること/shumoku-meetup1
corestate55
2
120
HRC_Frontend_Conference_Fukuoka_2026.pdf
ts020
0
650
synctest時代のhttptest Go 1.27で変わるHTTPサーバテストの裏側 / go conference2026 synctest and httptest
budougumi0617
1
2.8k
The Agent Builder Loop from Daily Work to OSS
minorun365
PRO
3
160
TinyGo 開発サイクルを高速化する:Go で作るエミュレータ入門
zozotech
PRO
1
680
コスト最適化の「めんどくさい」を AWS FinOps Agent でチョット楽にする
classmethod_kaz
0
330
Featured
See All Featured
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
560
Become a Pro
speakerdeck
PRO
31
6.2k
Navigating Team Friction
lara
192
16k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
670
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.4k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.6k
Raft: Consensus for Rubyists
vanstee
141
7.7k
The Limits of Empathy - UXLibs8
cassininazir
1
660
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
270
How to Talk to Developers About Accessibility
jct
2
540
Thoughts on Productivity
jonyablonski
76
5.4k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2.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を(可能な限り)用いないので、以下の恩恵が得られる。 - エラーハンドリングが容易。 - 型安全。 -
エラーのテストが容易。 是非、使ってみてください!!!!