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
Kodak
February 05, 2023
Technology
5.6k
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によるクリエイティブ生成を行う上での試行錯誤
plaidtech
PRO
0
150
LTのテーマ どうきめてる?〜5つの型と私のやり方〜
yama3133
1
110
アプリログインとWeb認証基盤をつなぐ ASWebAuthenticationSession 作法
shimastripe
1
330
登壇の自信を奪う3匹のオバケ / 3 Ghosts That Rob You of Your Confidence in Public Speaking
pauli
9
980
AI時代、データエンジニアが一番おもろい
genshun9
0
630
開発投資の期待値を上げるプロダクトロードマップづくり ~プロダクトエンジニアが越境して事業を伸ばす~
kekekenta
1
190
Issue 駆動でスペシャリストの意図を届ける、AI 実装のアクセシビリティ向上
thkt
0
120
作品が生態系になった ─ Mini Tokyo 3D から世界へ
nagix
0
190
AgentCore Runtime上にAgentic Coding基盤を構築・展開する際の設計ポイントと限界点 / Design considerations and limitations when building an agentic coding platform on AgentCore Runtime
har1101
4
210
GoのInterface内部構造から学ぶ!最高パフォーマンスを出すコード設計
yappli_developers
0
110
AI に書かせたその API、 “信頼” できますか?
nagix
0
110
目の前の楽しいが人生を変える - コミュニティの螺旋の歩き方と楽しむコツ / change your life
soudai
PRO
5
630
Featured
See All Featured
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
540
We Are The Robots
honzajavorek
0
380
The browser strikes back
jonoalderson
0
1.7k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
540
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
YesSQL, Process and Tooling at Scale
rocio
174
15k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
580
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
2
280
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
330
Automating Front-end Workflow
addyosmani
1369
210k
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を(可能な限り)用いないので、以下の恩恵が得られる。 - エラーハンドリングが容易。 - 型安全。 -
エラーのテストが容易。 是非、使ってみてください!!!!