Programming Field

typeof 演算子 - TypeScriptキーワード一覧

スポンサーリンク

typeof キーワードのうち、「式の値(評価された値)が属する型の種類を文字列で返す演算子」としての typeof について説明しています。

概要

「typeof」キーワードは、単一の式を受け付ける演算子(単項演算子)として扱われます。

typeof expression

expression に式を指定すると、この演算子を含む全体の結果は expression の値に基づいた型の名前(文字列)となります。この型の名前は 'undefined' や 'object' など仕様で定められており、これを利用してオブジェクトの内容を細かく判定することは出来ませんが、宣言されていない変数名を指定することができるため、「undefined かどうか」などの処理の場合分けにおいては重要な役割を持ちます。

typeof 演算子の仕様

構文

(参照: ES2015 - 12.5.6 The typeof Operator)

UnaryExpression: typeof UnaryExpression
UnaryExpression: typeof UnaryExpression
キーワード「typeof」に続けて単一の式を指定します。変数、定数、オブジェクトのメンバー、関数の戻り値などが考えられます。
式には原則として括弧「( )」は不要ですが、式の中に「+」などの別の演算子を含める場合は「( )」で括る必要があります。
なお、「typeof ...」の式全体はそれ自身が単一の式として扱われます。(理論上「typeof typeof ...」などとすることもできますが、ほぼ意味を成しません。)

意味

この演算子の評価結果は、「typeof」に続く UnaryExpression の評価結果に基づいて以下の文字列になります。

評価結果文字列TSでの型のnarrow(制約)先
(存在しない変数などの解決できない参照)'undefined'undefined
「undefined」値'undefined'undefined
「null」値'object'(状況依存)
「Boolean」値'boolean'boolean
「Number」値'number'number
「Symbol」値 (ES2015以前には存在しません)'symbol'symbol
「String」値'string'string
呼び出し可能なデータ(「[[Call]]」内部フィールドを実装)'function'Function
外部データ(スクリプトエンジンが提供するもの)(実装依存)(なし)
その他'object'(状況依存、上記に挙げた型を除き、nullを含む)

※ 「null 値」は 'null' にはなりません。「null 値」かどうかの判定方法は後述の詳細およびnull キーワードもご覧ください。

なお、TypeScriptでは上述の仕様に基づき、評価結果の型が「'string' | 'number' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function'」になります(TypeScript PR #13791)。これに加え、「型ガード」を行う演算子の1つとして以下のように利用されます(参照: TypeScript/spec.md 「4.24 Type Guards」)。

  • typeof x === s のガード(条件式)において s が 'string', 'number', 'boolean' (仕様には明記は無いが 'symbol', 'undefined', 'object', 'function' も利用可能) のいずれかとき:
    • ガードが true のとき、x は「x の型のsubtype(部分型)」という条件で(s に)対応するプリミティブ型(および Function 型)として扱われます。
      • 「『x の型のsubtype』という条件で」という制約があるため、「x の型のsubtype」にプリミティブ型が入らない場合は「never 型」となります。
    • ガードが false のとき、x の型から対応するプリミティブ型が取り除かれます。
  • typeof x === s のガード(条件式)において s が上記の場合を除く文字列のとき:
    • このケースの場合、s が「typeof」の結果の型に当てはまらないため、「常に結果が false となる式」と扱われエラーになります。
  • typeof x !== s のガード(条件式)において s が文字列のとき:
    • ガードが true のとき、x は「typeof x === s」の false の場合に基づいた型の制約(narrow)が行われます。
    • ガードが false のとき、x は「typeof x === s」の true の場合に基づいた型の制約が行われます。

キーワード情報

詳細

「型クエリー」はTypeScriptにおいて識別子から型を参照する際に用いる構文です。通常では、型を指定するときは「型として」定義されている識別子のみが使用でき、変数や関数などの「値として」定義されている識別子は利用できません。そこで、「typeof」キーワードで始まる型クエリーの構文を用いることで、変数や関数などの識別子から、その識別子が持つ型を利用することができます。

let myForm: HTMLFormElement;

// 第1引数が文字列または数値を受け付ける関数
function getMyFormElement(nameOrIndex: string | number) {
    if (typeof nameOrIndex === 'string') {
        // ここでは nameOrIndex は「string」型として扱える
        // (「namedItem」は「string」型を受け付けるので型ガードが無いと指定できない)
        return myForm.elements.namedItem(nameOrIndex);
    } else {
        // ここでは nameOrIndex は「number」型として扱える
        // (「item」は「number」型を受け付けるので型ガードが無いと指定できない)
        return myForm.elements.item(nameOrIndex);
    }
}

※ JSでも上記のような「number」や「string」などの判定を行うことは可能であり、特に上記のように呼び出すメソッドが変わる場合などはこのような分岐処理が必要になり、誤って実装した場合実際に実行するまで気付かなくなる可能性があります。(TypeScriptは実装の誤りを静的に判定・検出できるというメリットがあります。)

注意点としては、「typeof」では「undefined」の判定は可能ですが、「null」の判定はできないため、

// 第1引数が省略可能でnullable、かつ文字列または数値を受け付ける関数
function performSomething(data?: string | number | null) {
    if (typeof data !== 'undefined') {
        // ここでは data はまだ null である可能性がある
        pushText(data.toString()); // ERROR: TS2531: null の可能性がある
    }
}

というコードではエラーになります。そのため、別途nullかどうかの判定を行う必要があります。

// 第1引数が省略可能でnullable、かつ文字列または数値を受け付ける関数
function performSomething(data?: string | number | null) {
    if (typeof data !== 'undefined' && data !== null) {
        // ここでは data は「string」または「number」のどちらかに絞られる
        pushText(data.toString()); // 「string」も「number」を引数なしの「toString」が使えるので常に文字列を渡すことができる
    }
}

※ 上記における「typeof data !== 'undefined'」という記述は「data !== void 0」または(よほど特殊な環境でない限り)「data !== undefined」とも記述することができます。ただしtypeof data !== 'undefined' && data !== null」という判定式を単純に「data」としてしまうと、「undefined」や「null」だけでなく、文字列の「''」や数値の「0」までも除外してしまうため注意が必要です。(「data != null」でも可能ですが、「==」や「!=」はあまり推奨されない傾向にあります。)

特に、利用したい変数が「unknown 型」である場合、そのままでは利用できず型ガードがほぼ必須になるため、「typeof」を使う必要が生じる可能性があります。

let myObj: any;

// 第1引数が「string 型」か「number 型」か「symbol 型」のいずれかであれば特定の値を、
// それ以外の場合は undefined を返す関数
function getByData(data: unknown) {
    // 「unknown 型」はそのままでは利用できないので型ガードを行う
    if (typeof data === 'string' || typeof data === 'number' || typeof data === 'symbol') {
        // 「string」「number」「symbol」であればプロパティーキー(インデックスシグネチャ)として利用できる
        return myObj[data];
    } else {
        // それ以外の場合は undefined とする
        return void 0;
    }
}

なお、「typeof」は「Functionかどうか」、すなわち「呼び出し可能(callable)かどうか」の判定にも利用できます。(多くの場合、「呼び出し可能(callable)かどうか」の判定に限り、「instanceof」を利用した判定(「x instanceof Function」)も可能です。)

// 何かが設定されている変数
let p: unknown;
  .
  .
  .
// callableかどうか判定(「p instanceof Function」でも基本的には可)
if (typeof p === 'function') {
    // 「p」がcallableなので呼び出し構文が利用可能
    p();
}

※ ES2015の仕様上、「typeof x === 'function'」と「x instanceof Function」は直接的にはイコールではありませんが、少なくともES2015の仕様上すべてのcallableなデータは「Function」オブジェクトのprototypeまたはその派生を持つことになっているため、実質的にはほぼ等価に利用できます。ただし、関数オブジェクトのprototypeが「Object.setPrototypeOf」で書き換えられている場合はこの限りではありません。