29
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TypeScriptでオブジェクトかどうかを判定する関数

Posted at

本稿ではTypeScriptで値がオブジェクトかどうかを判定する関数の考え方を述べる。

そもそもオブジェクトとは?

TypeScriptはJavaScriptのスーパーセットなので、「TypeScriptにおけるオブジェクトとは?」という疑問は「JavaScriptのオブジェクトとは?」と言い換えることができる。

JavaScriptのオブジェクトとは?

JavaScriptのオブジェクトを考える上で、オブジェクトじゃないデータ型は何があるかを知る必要がある。

最新の ECMAScript 標準仕様では 7 つのデータ型が定義されています。

  • 6 種類のプリミティブデータ型:
    • Boolean
    • Null
    • Undefined
    • Number
    • String
    • Symbol (ECMAScript 6 の新データ型)
  • Object

── JavaScript のデータ型とデータ構造 - JavaScript | MDNより抜粋

これによれば、FunctionArrayもオブジェクトのひとつになる。

TypeScriptのobjectとは?

TypeScriptにはobject型があるが、これがJavaScriptのオブジェクトと一致するのか見ておこう。

公式ドキュメントによれば、「objectはプリミティブ型ではないものを指す型で、例えば、numberstringbooleansymbolnullundefinedではないもの」ということなので、JavaScriptのオブジェクトと同じだということが分かる。

object is a type that represents the non-primitive type, i.e. anything that is not number, string, boolean, symbol, null, or undefined.

── Basic Types · TypeScript

typeof演算子とデータ型の分類の違い

JavaScriptのtypeof演算子は、与えられたオペランドの型を表す文字列を返す。

typeof operand

例えば、typeof {a: 1}の演算結果は"object"になる:

typeof {a: 1} //=> "object"

前述したとおり、JavaScriptには7つのデータ型区分が定義されているが、それらとtypeofが返す文字列とは必ずしも1対1で一致しない。

返値 7つのデータ型区分
未定義 undefined Undefined (プリミティブ)
Null object Null (プリミティブ)
真偽値 boolean Boolean (プリミティブ)
数値 number Number (プリミティブ)
文字列 string String (プリミティブ)
シンボル (ECMAScript6 で新しく導入) symbol Symbol (プリミティブ)
ホストオブジェクト (provided by the JS environment) 実装に依存 ???
関数オブジェクト (implements [[Call]] in ECMA-262 terms) function Object
その他のオブジェクト object Object

ここで注目したいのが下記の2点だ。

  • nullは7つのデータ型区分ではNullだが、typeof演算子ではobjectになる。
  • 関数オブジェクトは7つのデータ型区分ではObjectだが、typeof演算子ではfunctionになる。

したがって、typeof value === 'object'trueなら、valueが「JavaScriptのオブジェクトである」とは言えないわけだ。valuenullのときや関数のときが反例になる。

オブジェクトかどうかを判定する関数

typeofの仕様を踏まえた上で作るオブジェクトかどうかを判定する関数は次のようになる:

const isObject = (x: unknown): boolean =>
  x !== null && (typeof x === 'object' || typeof x === 'function')

タイプガードに使うことも考えると、戻り値を次のようにしておくと良さそうだ:

const isObject = (x: unknown): x is object =>
  x !== null && (typeof x === 'object' || typeof x === 'function')

オープンソースのライブラリはどういう実装になってる?

参考までに、実績のあるオープンソースライブラリで、オブジェクトかどうかを判定する関数がどうなっているかも見ておこう。

underscore

JavaScriptでの実装:

underscore.js
  // Is a given variable an object?
  _.isObject = function(obj) {
    var type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
  };

── GitHubのunderscore.jsより抜粋。

TypeScriptでの定義:

underscore/index.d.ts
        isObject(object: any): boolean;

── DefinitelyTypedのunderscore/index.d.tsより抜粋。

lodash

JavaScriptでの実装:

isObject.js
function isObject(value) {
  const type = typeof value
  return value != null && (type == 'object' || type == 'function')
}

── GitHubのisObject.jsより抜粋。

TypeScriptでの定義:

lodash/common/lang.d.ts
        isObject(value?: any): value is object;

── DefinitelyTypedのlodash/common/lang.d.tsより抜粋。

Angular.js

JavaScriptでの実装:

angular.js
function isObject(value) {
  // http://jsperf.com/isobject4
  return value !== null && typeof value === 'object';
}

── GitHubのangular.jsより抜粋。

  • functionを考慮していないのは仕様なのかどうなのかは不明。

TypeScriptでの定義:

angular/index.d.ts
        isObject(value: any): value is Object;
        isObject<T>(value: any): value is T;

── DefinitelyTypedのangular/index.d.tsより抜粋。

29
19
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
29
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?