TypeScript で catch の「Object is of type 'unknown'」が解決できない
TypeScript 4.4 以降は useUnknownInCatchVariables(strict で既定有効)により catch の変数が unknown 型になる。
instanceof や型ガードで絞ってからプロパティへアクセスする。
#typescript#catch#unknown#error-handling#strict
要約
Object is of type 'unknown'. が catch ブロックで出るのは、TypeScript 4.4 以降の useUnknownInCatchVariables(strict: true で既定有効)により、catch (e) の e が any ではなく unknown 型になったため。unknown はそのままでは何もできないので、型を絞ってから 使う。
これは安全側に倒した正しい挙動で、握り潰さず対処する。
よくある原因
- strict 既定:
strict: trueに連動してuseUnknownInCatchVariablesが有効になっている - 未ナローイングのアクセス:
catch (e) { console.log(e.message) }と直接プロパティを読んでいる - reject 値の前提:
Promiseの reject やthrowされる値をany前提のコードで扱っている - 旧コードの移行: 4.4 より前に書かれた古い例外処理コードをそのまま移している
解決策
1. instanceof で絞る
try {
doWork();
} catch (e) {
if (e instanceof Error) {
console.error(e.message);
} else {
console.error(String(e));
}
}Error 以外が throw される可能性も String(e) で受ければ漏れない。
2. 型ガード関数で検証する
function hasMessage(e: unknown): e is { message: string } {
return typeof e === "object" && e !== null && "message" in e;
}
try {
doWork();
} catch (e) {
if (hasMessage(e)) console.error(e.message);
}e is { message: string } の述語型で、必要な形だけを安全に取り出す。
3. any の多用を避ける
個別に catch (e: any) と書くこともできるが、型安全をその場で捨てることになる。
例外処理こそ未知の値が来る場所なので、unknown を絞る方針を基本にする。
詳しい背景は TypeScript 4.4 リリースノート を参照。
4. 移行期だけ設定で戻す
{
"compilerOptions": {
"useUnknownInCatchVariables": false
}
}大量の旧 catch を一度に直せないときの一時退避。
各オプションの意味は tsconfig の useUnknownInCatchVariables にある。
最終的には unknown を絞る形へ寄せる。