🦕
deno lint plugin で例外処理に関するルールを作った
do から始まる関数は try catch しないといけない、という deno-lint-plugin を作ってみた。
動機
- Gemini/Claude は例外設計が下手くそ
- 全部 try catch して握り潰す
- 明示的に例外を表現したい
- neverthrow はやりすぎ
- TS の型シグネチャは例外が現れないので難しい
ネタ元: vscode の doGet~
vscode のコードを読んだ時に見たパターンで、do から始まる関数は例外を吐くことが明示されてて、たしかにわかりやすかった。
async function doGetLogs(fileService: IFileService, logs: ILogFile[], curFolder: URI, logsHome: URI): Promise<void> {
...
}
もっというと Java の検査例外
public void throwableMethod(String name) throws Exception {...}
deno-lint-plugin で実装してみた
{
"lint": {
"plugins": ["jsr:@mizchi/deno-lint-plugin-do-try"]
}
}
2 つのルールを実装。
-
do-try/require-try-catch-for-do-functions
- do から始まる関数の呼び出しは、必ず try catch で囲む必要がある
-
do-try/throw-needs-do-prefix
- throw を含む関数名は
do~
である必要がある。
- throw を含む関数名は
eslint でないのは、単に deno-lint-plugin が作ってみたかったから...
async function doSomething() {}
const obj = { doSomething };
// NG
doSomething();
// NG
await doSomething();
// NG
await obj.doSomething();
try {
const run = async () => {
// NG
await doSomething();
};
await run();
} catch (error) {
console.error("An error occurred:", error);
}
try {
// OK
doSomething();
} catch (error) {
console.error("An error occurred:", error);
}
// NG
// deno-lint-ignore no-unused-vars
function throwableFunction() {
if (Math.random() > 0.5) {
throw new Error("Random error");
}
return "data";
}
// OK
// deno-lint-ignore no-unused-vars
function doThrowableFunction() {
if (Math.random() > 0.5) {
throw new Error("Random error");
}
return "data";
}
実行結果
$ deno lint examples/main.ts
error[do-try/require-try-catch-for-do-functions]: Function 'doSomething' starts with 'do', so it must be wrapped in a try-catch block
--> /home/mizchi/sandbox/deno-plugin/examples/main.ts:4:1
|
4 | doSomething();
| ^^^^^^^^^^^^^
error[do-try/require-try-catch-for-do-functions]: Function 'doSomething' starts with 'do', so it must be wrapped in a try-catch block
--> /home/mizchi/sandbox/deno-plugin/examples/main.ts:5:1
|
5 | await doSomething();
| ^^^^^^^^^^^^^^^^^^^
error[do-try/require-try-catch-for-do-functions]: Function 'doSomething' starts with 'do', so it must be wrapped in a try-catch block
--> /home/mizchi/sandbox/deno-plugin/examples/main.ts:6:1
|
6 | await obj.doSomething();
| ^^^^^^^^^^^^^^^^^^^^^^^
error[do-try/require-try-catch-for-do-functions]: Function 'doSomething' starts with 'do', so it must be wrapped in a try-catch block
--> /home/mizchi/sandbox/deno-plugin/examples/main.ts:10:5
|
10 | await doSomething();
| ^^^^^^^^^^^^^^^^^^^
error[do-try/throw-needs-do-prefix]: Function 'throwableFunction' contains throw statement, so it must start with 'do'
--> /home/mizchi/sandbox/deno-plugin/examples/main.ts:24:1
|
24 | function throwableFunction() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
25 | if (Math.random() > 0.5) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
26 | throw new Error("Random error");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
27 | }
| ^^^
|
28 | return "data";
| ^^^^^^^^^^^^^^^^
|
29 | }
| ^
真面目にプロダクションで使ったわけじゃないが、自分の意図は表現できてるので、微修正しながら使っていく。
おまけ: 作り方
まず AI にこいつを読ませる。
あとは Few Shots で valid なコードと invalid なコードを与えて、夕飯食べ終わったらできてた。
Discussion