本記事はZennに投稿している記事の再投稿になります。
https://zenn.dev/axoloto210/articles/advent-calender-2023-day16
2種類のtypeof
値の型情報を取得するための演算子としてtypeofという演算子があります。このtypeofにはJavaScriptで使用されているtypeofとTypeScriptの型演算子としてのtypeofがあります。
JavaScriptのtypeof演算子
JavaScriptにおけるtypeof演算子は、値の型を文字列で返す演算子です。
JavaScriptの型について、typeofは以下のような値を返します。
| Type |
typeofの返り値 |
|---|---|
| Null | "object" |
| Undifined | "undefined" |
| Boolean | "boolean" |
| Number | "number" |
| String | "string" |
| Bigint | "bigint" |
| Symbol | "symbol" |
| 関数オブジェクト | "function" |
| その他のオブジェクト | "object" |
以下のコードのようにオブジェクトの型をプロパティを含めて詳しく見たいと思っても、"object"しか返ってきません。これは、JavaScriptのtypeof演算子が文字列を返しているからというわけですね。
const obj = {
num: 1,
str: 'one'
}
console.log(typeof obj)// "object"
typeofはnullに対しても"object"を返すという点にも注意が必要です。
console.log(typeof {num: 1} === typeof null) //true
この挙動はJavaScriptのバグなのですが、"null"を返すよう修正を行なってしまうと既存のコードが破壊されることが懸念されるため、修正はされない方針となっているようです。
In JavaScript, typeof null is 'object', which incorrectly suggests that null is an object (it isn’t, it’s a primitive value, consult my blog post on categorizing values for details). This is a bug and one that unfortunately can’t be fixed, because it would break existing code. Let’s explore the history of this bug.
TypeScriptのtypeof型演算子
TypeScriptにも値の型情報を得るための演算子としてtypeofがあります。
こちらはtypeof型演算子(Typeof Type Operator)と呼ばれ、typeof演算子と区別されています。
const obj : {
num: number
str: string
} = {num: 1, str: 'one'}
// type T = {
// num: number;
// str: string;
// }
type T = typeof obj
//const t: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
const t = typeof obj
このコード例では、型Tはオブジェクトのプロパティを含めて詳細な型情報をtypeof型演算子で取得できています。
一方、typeof演算子によってobjの型に関する文字列を代入されている変数tはtypeof演算子が返しうる文字列のユニオン型であると型推論がされています。
typeof objがどのような文字列を返すかはJavaScriptのtypeofが実行されるまでわからないため、このような型推論がされているわけですね。
typeof演算子とtypeof型演算子の見分け方
同じ綴りの演算子があると混乱してしまいそうですが、typeof型演算子はリテラル型の"foo"や123と同様に、型注釈などの型を記述する場面でのみ使用することが可能ですので、どこに記述されているかに着目することで見分けられます。
const obj = {str: 'foo'}
//const obj2: { str: string; }
const obj2: typeof obj = {str: typeof obj}
console.log(obj2.str) //"object"
上のコード例で出てくる1つ目のtypeofは型注釈として使用されていますので、型演算子の方のtypeofであるとわかります。
2つ目のtypeofについては、オブジェクトリテラルに含まれるstrプロパティの値として記述されていますので、typeof演算子の方であるとわかります。
TypeScriptにはtypeofの他にもリテラル型など、JavaScriptでは別の意味をもつが同じ書き方がされる機能がいくつかあります。
どちらの機能なのかわからなくなった時には、記述位置が型が期待される箇所なのか、値が期待される箇所なのかを考えてみると見分けがつくはずです。