LoginSignup
22
13

More than 5 years have passed since last update.

unknown型とタイプガードと私

Last updated at Posted at 2019-05-03

自戒と備忘録として。typescript 3.4.5 で以下は確認しています。

typescript 3系で unknown 型が導入され、オッ外部からのデータを受け付ける箇所から any が撲滅できるのではないか、みたいなことを思ったことがあるわけです。

ただ、

type User = {
  name: string;
  age: number;
};

const data: unknown =
   JSON.parse(`{ "user": { "name": "user", "age": 18 } }`);

こういう unknown なデータがあった時に、なんとなく以下のように書けそうに思うけどうまくいかない。

if(data && typeof data === 'object'){
  if("user" in data){
    const user = data.user; // Error, data["user"] でもいっしょ
    if (
      user &&
      typeof user === "object" &&
      typeof user.name === "string" &&
      typeof user.age === "number"
    ) {
      doSomethingFor(user)
    }
  }
}

プロパティ 'user' は型 'object' に存在しません。ts(2339) とそっけなく言われてしまう。それはそうかもしれないけど、if("user" in data) してるんだからそこは推論してほしい気持ちはある。一度 any とか {} にキャストしてしまうのも一つの方法だが、率直に意図が読みづらいコードになるのは間違いない。

ここはタイプガード関数を通すとよいみたい。

const hasUser = (d: any): d is { user: User } => {
  if (!d) return false;
  if (d.user && typeof d.user === "object") {
    if (
      typeof d.user.name === "string" &&
      typeof d.user.age === "number"
    ) {
      return true;
    }
  }
  return false;
};

// 以下は通る。
if (hasUser(data)) {
  const user: User = data.user;
}

あれ、 any が出現しているぞ・・・? でもこの hasUser が公開する受け入れ可能な引数の型が any なのは自然なのだ。unknown しか受け付けない関数とか、(外から見たら)意味がわからない。

というわけで、

  1. unknown は any を返す関数の戻り値を受け取るときに使う
  2. unknown で受けたあとは明示的に型チェックを行うタイプガードを通す
  3. プリミティブでないことが期待されるデータを unknown で受けたら、どこかで any が混入することにはそういうものです

というところに落ち着いたがまだ釈然としていない。

個人的には if("user" in data && typeof data.user) は許してほしかったところだけど。でも、 unknown の問題というより typescript の object 型の扱いに問題があるような気がしてきた。

関連 Issue については引き続き眺めていきたいところ。

22
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
13