かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

TypeScript Object 型を判定したい

any 型で入ってきたデータのプロパティにアクセスしようとすると ESLint に怒られるので object 型だと判定したかった

value.foo

=> Unsafe member access .foo on an `any` value. eslint@typescript-eslint/no-unsafe-member-access

JavaScript の Object

オブジェクトを除くすべての型は、言語の最下層で直接表現される不変値を定義しています。これらの型の値を プリミティブ値 と呼びます。
cf. JavaScript のデータ型とデータ構造 - JavaScript | MDN

JavaScript のプリミティブ型
typeof の返値
Null型 null "object"
Undefined型 undefined "undefined"
論理型 boolean "boolean"
数値型 number "number"
長整数型 bigint "bigint"
文字列型 string "string"
シンボル型 symbol "symbol"

プリミティブ型以外は Object なので、Array も Function も Date も RegExp も Object という扱い

isObject 関数を作成する

Type Guard関数をユーザーが定義することができます。これは、someArgumentName is SomeTypeを返す関数です。
cf. 型ガード - TypeScript Deep Dive 日本語版

返り値が T is object な Type Guard 関数を作成する

export const isObject(value: unknown): value is object => {
  const type = typeof value;
  return value !== null && (type == 'object' || type == 'function');
}

isObject (Type Guard関数) が true になる Object 型の変数のプロパティにアクセスできない問題

import { isObject } from './isObject';
if (isObject(myObj)) {
  myObj.foo; // ここで TypeError
}

=> Property 'foo' does not exist on type 'object'. ts(2339)
object 型だが foo プロパティがあるとは限らないので TypeError になっている

Type Guard の型を {[key: string]: unknown}Record<string, unknown> にすれば良い

export const isObject(value: unknown): value is Record<string, unknown> => {
  const type = typeof value;
  return value !== null && (type == 'object' || type == 'function');
}

👇 これで Object のプロパティへのアクセスが OK になる

import { isObject } from './isObject';
if (isObject(myObj)) {
  myObj.foo; // OK
}

{key: value} なオブジェクトに限定したい場合は constructor を使うと判別できる

JavaScript の Object は Array や function, Date なども含んでいて . でプロパティにアクセスできるから問題は無いと思うが Type Guard の value is Record<string, unknown> は厳密ではないよな〜って感じなので {key: value} なオブジェクトで判定したい場合

export const isObject(value: unknown): value is Record<string, unknown> => {
  return value !== null && typeof value === 'object' && value.constructor === Object;
}

cf. JavaScript `{}` なオブジェクトだけを判定したい - かもメモ

TypeScript化した

おわり


[参考]