TypeScriptのArray.filterで'Object is possibly undefined'を消したい
2019-03-26

TypeScriptのArray.filterで'Object is possibly undefined'を消したい

TypeScriptの小ネタです。

次のような基本的なArrayのmap処理を考えてみましょう。
TypeScriptのコンパイラオプションにはstrictNullChecksを入れています。

interface User {
  id: number;
  name?: string;
}

const users: User[] = [
  { id: 1, name: 'aaa' },
  { id: 2  },
  { id: 3, name: 'bbb' },
];

users
  .filter(user => Boolean(user.name))
  // Object is possibly 'undefined'... why?
  .map(user => console.log(user.name.length));

UserクラスのnameはOptionalなので、一度filter関数を通過させてundefinedのものを除外しています。
しかし、TypeScriptのコンパイラはしつこくObject is possibly 'undefined'と言ってきます。

今回は、これをどうにかしたいです。

User-Defined Type Guardsを使う

このエラーを解消するためにはType Guardsを使って、コンパイラに型情報を追加で教える必要があります。

1つめのやり方は、nameがOptionalではない新しいTypeを作る方法です。

...

// Userを継承した新しいTypeを作る
interface ConfirmedUser extends User {
  name: string;
}

users
  .filter((user: User): user is ConfirmedUser => Boolean(user.name))
  .map(user => console.log(user.name.length));

もう1つのやり方は、TypeScriptにビルトインされているmapped-typeのRequired<T>を使います。

...

users
  .filter((user: User): user is Required<User> => Boolean(user.name))
  .map(user => console.log(user.name.length));

Required<T>を使う場合は全てのプロパティがrequiredになるので、部分的にOptionalが残る場合は、最初のextendsで新しいTypeを作るほうがいいと思います。

疑問に思ったので、ここで聞いてみた内容でした。