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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
あけの
July 03, 2022
Programming
3k
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
760
VSCode Remote Containers のすすめ
akeno
0
350
設計とテストの必要性について考える
akeno
1
320
Other Decks in Programming
See All in Programming
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
180
AI時代のUIはどこへ行く?その2!
yusukebe
22
7.4k
net-httpのHTTP/2対応について
naruse
0
500
AIだと陥りがちなJakarta EE最新技術への移行時の落とし穴と解決策
tnagao7
0
110
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
400
Even G2とAWSで推しのエージェントを召喚しよう!
har1101
1
120
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
410
The NotImplementedError Problem in Ruby
koic
1
840
AI 輔助遺留系統現代化的經驗分享
jame2408
1
780
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.3k
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
200
Hunting Vulnerabilities in Symfony with LLMs
vinceamstoutz
0
550
Featured
See All Featured
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.1k
Odyssey Design
rkendrick25
PRO
2
700
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.2k
Why Our Code Smells
bkeepers
PRO
340
58k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.8k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
3.5k
Done Done
chrislema
186
16k
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
Marketing to machines
jonoalderson
1
5.5k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
250
Agile that works and the tools we love
rasmusluckow
331
21k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
220
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 エラーの原因を保持しやすくなった エラー処理は雑になりやすい部分なので、 安全かつ後から原因を追跡できるように実装していきたい