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のエラー処理(ES2022の新機能を添えて)
Search
あけの
July 03, 2022
Programming
3.1k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
TypeScriptのエラー処理(ES2022の新機能を添えて)
あけの
July 03, 2022
More Decks by あけの
See All by あけの
Reactハンズオンラーニングを読んだので感想を語る
akeno
1
710
oapi-codegenを使ってみた
akeno
0
2.9k
こんな案件は嫌だ(※個人の感想です)
akeno
1
240
SQLアンチパターンから学ぶテーブル設計
akeno
0
770
VSCode Remote Containers のすすめ
akeno
0
360
設計とテストの必要性について考える
akeno
1
320
Other Decks in Programming
See All in Programming
AI時代のPHPer生存戦略 ~「言語、もうなんでもよくない?」に本気で向き合う~
vivion
0
350
AWS DevOps AgentのAzure接続機能を検証して見えた活用法/Use Cases Verified for the AWS DevOps Agent's Azure Connectivity Feature
masakiokuda
1
230
FDEが実現するAI駆動経営の現在地
gonta
2
280
AI時代に設計が 最大の生産性レバーになる 意図駆動開発とデータを消さない設計|Don't Delete Your Data or Your Intent — Design as the Deepest Lever in the AI Era
tomohisa
1
840
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
590
わからない話を追いかけたら、プログラミング言語を作る側にいた
ydah
3
500
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
470
jsmini JavaScript Engine を作ってみた話
yosuke_furukawa
PRO
0
320
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
1
510
源内ハンズオン概要編
hideg
0
170
仕様駆動開発の消費期限
watany
20
8.2k
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
690
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
50
15k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Test your architecture with Archunit
thirion
1
2.3k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
The Language of Interfaces
destraynor
162
27k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
410
Art, The Web, and Tiny UX
lynnandtonic
304
22k
The Limits of Empathy - UXLibs8
cassininazir
1
600
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
280
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
240
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
790
First, design no harm
axbom
PRO
2
1.2k
Transcript
@akeno_0810 2022.07.03 TypeScriptのエラー処理 (ES2022の新機能を添えて) Web Creator Meetup in KANSAI #2
自己紹介 About me akeno (@akeno_0810) Webエンジニア歴2年くらい Rust, API/コード設計, DevOps/開発の効率化 触っている技術
最近興味のある分野
話すこと About this talk f TypeScriptのエラー処理の紹g f エラーを返り値とする場合の型のハマりどこe f ES2022のHard
Privatb f ES2022のError Causb f 追加情報を持たせていい感じに出力する
TypeScriptのエラー処理
TypeScriptのエラー処理 エラー処理 →ある関数で処理続行不可能になった場合にどうハンドリングするか? 言語によって差がある Java→Exceptionをthrowする Rust→Result型やOption型を用いる Go→返り値でエラーを返す TypeScriptではどうするのがいいのか? try throw
new catch { HogeException(); } (HogeException e) { e.printStackTrace(); } res, err := HogeFunc() err != { fmt.Errorf(“%v”, err) } if nil fn hogeFunc() -> Result<i32, ParseIntError> { Err() } let result = hogeFunc() match result { Ok(n) => !( , n), Err(e) => !( , e), } println println "n is {}" "Error: {}"
TypeScriptのエラー処理 https://qiita.com/kabosu3d/items/680728362314f51bdcb0 XP nullやundefinedを返り値とする function : | if return return
() { (error) { ; } “success”; } hoge string null null Goo 何も考えないでいいので 呼び出し元でnullを考慮する必要があるので安全 Ba エラーが起こった以上の情報が得られない
TypeScriptのエラー処理 2. 例外をスローする GooF 詳細なエラー情報を呼び出し元に渡せる BaF 呼び出し元からはエラーが起こるかどうか読み取れなA
どのようなエラーが返ってくるかわからなA try-catchを忘れると永遠にcatchされずに落ちる function : if throw new return try catch if instanceof () { (error) { (); } “success”; } { (); } (e) { (e ){ console. (e); } } hoge hoge Error log string Error
TypeScriptのエラー処理 3. エラーを返り値とする Gooc d 詳細なエラー情報を呼び出し元に渡せB d 呼び出し元は返ってくるエラーを考慮する必要がある Bac d
エラーを返す側も呼び出し側も記述が冗長 function : | if return new return const = if instanceof () { (error) { (); } “success”; } (); (h ) { console. (e); } hoge Error hoge Error log string Error h
TypeScriptのエラー処理 4. Option/Result型を用いる Goot 呼び出し元は返ってくるエラーを考慮する必要がある Bat 言語でのサポートがなD
アプリケーション全体がライブラリに依存する
TypeScriptのエラー処理 nw nullやundefinedを返り値とすq w 例外をスローする エラーの詳細度や型安全の観点から厳しい 4. Option/Result型を用いる 特定のライブラリにアプリケーション全体が依存する状況は避けたい 個人的には殆どの場合で
3. エラーを返り値とする を使っている。
エラーを返り値とする場合の 型のハマりどころ
エラーを返り値とする場合の型のハマりどころ エラーを返り値とすることで型安全…と思いきやそうでもない エラー定義の方法によっては型の縛りが効かない場合がある class extends class extends return if instanceof
== { } { } (): HogeError { (); } ( () ) { console. (“HogeError FugaError”) } HogeError FugaError hoge FugaError hoge FugaError log Error Error function // エラーにならない functionの返り値の型はStructural Subtyping(ダックタイピング) によって判断される →お互いの実装が同じなので型を満たしていると判断される →違う型を意図していたが同じ型として扱われた instanceofは返り値のプロトタイプチェーンを見ている →実際返ってきているのはFugaErrorなのでtrue 型が壊れている状態
エラーを返り値とする場合の型のハマりどころ class extends readonly = class extends readonly = function
: return { // “HogeError”型 “HogeError”; } { // “FugaError”型 “FugaError”; } () { (); } HogeError FugaError hoge HogeError FugaError Error Error className className // エラー! 解決策 各クラスにreadonlyの文字列を持たせる →型推論によって`className`の型が絞り込まれる 関数で使用する際にその部分の型が異なる と判定されてコンパイルエラーとなる
ES2022のHard Private
ES2022のHard Private class extends readonly = class extends readonly =
{ “HogeError”; } { “PiyoError”; } HogeError PiyoError HogeError Error // “HogeError”型 // 継承している // “PiyoError”型 ... 定義できない! className className さらなる問題 継承元で定義されている型を継承先で変更できな string型で指定すると型判定ができない 困った… →継承先にプロパティを渡さなければ解決する! (ちなみにSoft PrivateではJavaScriptに変換された 際の挙動が異なるため実現できない)
ES2022のHard Private class extends readonly = class extends readonly =
{ “HogeError”; } { “PiyoError”; } HogeError PiyoError HogeError Error // “HogeError”型 // 継承している // “PiyoError”型が定義できる! #className #className Hard Privateを用いた解決 (https://ics.media/entry/220610/) Hard Private ES2022で追加されたプライベートプロパティの宣言 TypeScriptとしてはv3.8から使えた Soft Private のような宣言 この2つはトランスパイルの結果が異なる 継承を用いても型安全にエラーを処理できる! private hoge = “hoge”
ES2022のError Cause
ES2022のError Cause interface ?: interface ?: interface new ?: ?:
: ?: ?: : { ; } { ; } { ( , ) ; ( , ) ; } ErrorOptions Error Error Error ErrorConstructor ErrorOptions Error ErrorOptions Error cause cause message options message options string string ES2022のエラー周りの型定義 interface : : ?: interface new ?: : ?: : readonly : declare var : { ; ; ; } { ( ) ; ( ) ; ; } Error ; Error ErrorConstructor Error Error Error ErrorConstructor name message stack message message prototype string string string string string ちなみにES5ではこう コンストラクタにOptionsが渡せるようになっている
ES2022のError Cause つまりこういうことができる // ES2022 error cause // throw error
const = : : => const ... = if ! return ... return try throw new new catch if instanceof ( ) { { , , , } e; ( cause) { rest, name: e.name, msg: e.message}; {name: name, msg: message, cause: (cause)}; } { ( , {cause: ( )}); } (e) { (e ) { console. ( . ( (e))); } } printErr Error CustomErr printErr ResError ReqError ResError log printErr e name message cause rest JSON stringify "a" "b" エラーの入れ¥ 再帰的に取り出 いい感じに出力 深い階層で起こったエラー をWrapして、上の階層に 返すことが容易になった {"name":"Error","msg":"a","cause":{"name":"Error","msg":"b"}}
追加情報を持たせていい感じにする
追加情報を持たせていい感じにする export class extends : constructor : : : :
= { < , >; ( , { , } { ; < , > } ) { (message, { cause }); .obj obj; } } CustomError Record Error Record Error string unknown string string unknown super this obj message cause obj cause obj エラーには追加情報がつきもの ファイル名や実行された際の引数等、message: string で表現するには限界がある アプリケーション側でカスタムしたエラーを定義して、それを継承していく
追加情報を持たせていい感じにする 出力が面倒になってくるのでロギングライブラリを用いる https://github.com/pinojs/pino new new const = => ( ,
{ cause: ( ), obj: {hoge: } }); ({ formatters: { : ( , ) ({ level: label, }), }, browser: { asObject: , }, }); logger. (e); logger. ({e}); ResError ReqError pino level error error "a" "b" "huga" logger true label _ { time: 1656824412503, level: 50, obj: { hoge: "huga" } } { time: 1656824412503, level: 50, e: Error: a at file:///Users/**masked**/Projects/ts-error-demo/main.ts:37:11 Caused by Error: b at file:///Users/**masked**/Projects/ts-error-demo/main.ts:37:37 } errortraceとobjを一緒に出せたはずなのだが、 出来ない… with deno
追加情報を持たせていい感じにする { : , : , : , : ,
: { : , : , : , : { : } }, : } "level" "time" 1656825987852 "pid" 50218 "hostname" "err" "type" "message" "stack" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n "obj" "hoge" "msg" "error" "**masked**" "ResError" "a: b" "Error: a at Object.<anonymous> (/Users/**masked**/Projects/ts-error-demo-node/dist/main.js:31:11) at Module._compile (node:internal/modules/cjs/loader:1105:14) at Module._extensions..js (node:internal/modules/cjs/loader:1159:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Module._load (node:internal/modules/cjs/loader:827:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12) at node:internal/main/run_main_module:17:47 caused by: Error: b at Object.<anonymous> (/Users/**masked**/Projects/ts-error-demo-node/dist/main.js:31:47) at Module._compile (node:internal/modules/cjs/loader:1105:14) at Module._extensions..js (node:internal/modules/cjs/loader:1159:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Module._load (node:internal/modules/cjs/loader:827:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12) at node:internal/main/run_main_module:17:47" "huga" "a" with node.js
まとめ
Thank you! まとめ TypeScriptのエラー処理には実装の選択肢があ 型安全かつ使うハードルの低いものを選びたc TypeScriptの型は実装ベースで判断され
型を明確に異なるものにしたい場合は工夫が必0 ES2022(TS3.8)で入ったHard Privateが使え ES2022でError Causeが入っf エラーの原因を保持しやすくなった エラー処理は雑になりやすい部分なので、 安全かつ後から原因を追跡できるように実装していきたい