できない.dev

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 以降の useUnknownInCatchVariablesstrict: true で既定有効)により、catch (e)eany ではなく unknown 型になったため。unknown はそのままでは何もできないので、型を絞ってから 使う。
これは安全側に倒した正しい挙動で、握り潰さず対処する。

よくある原因

  1. strict 既定: strict: true に連動して useUnknownInCatchVariables が有効になっている
  2. 未ナローイングのアクセス: catch (e) { console.log(e.message) } と直接プロパティを読んでいる
  3. reject 値の前提: Promise の reject や throw される値を any 前提のコードで扱っている
  4. 旧コードの移行: 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 を絞る形へ寄せる。

この記事は役立ちましたか?