【TypeScript】厳密等価演算子を10選の実用コードで徹底解説

TypeScriptの厳密等価演算子を図解したイメージ TypeScript
この記事は約20分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

TypeScriptは、多くの開発者に支持されている強力な静的型付けスクリプト言語です。

この記事では、TypeScript初心者が必ず知っておくべき「厳密等価演算子」について、初心者のための徹底解説を行います。

10のサンプルコードを交えながら、基本から応用、注意点、カスタマイズ方法まで、詳しくわかりやすく学べる内容となっています。

厳密等価演算子はJavaScriptにも存在しますが、TypeScriptでの使用には特有の注意点やノウハウが求められます。

この記事を読むことで、TypeScriptでの厳密等価演算子の正しい使い方や、その背後にある仕組みを理解することができるでしょう。

では、まずは「厳密等価演算子」が何であるか、その基本的な理解からスタートしていきましょう。

●TypeScriptの厳密等価演算子とは

TypeScript、特に初心者の方々にとって、厳密等価演算子は非常に重要な概念の一つと言えます。

JavaScriptの仲間であるTypeScriptでも、厳密等価演算子は非常に重要な役割を持っています。

ここでは、その厳密等価演算子が何であり、その特性や利点、そして基本的な理解について詳しく解説していきます。

JavaScriptとTypeScriptにおける「等価」の概念には、基本的に二つの方法が存在します。

一つは抽象的な等価性を示す「==」、もう一つは厳密な等価性を示す「===」です。

今回の主題である厳密等価演算子は、後者の「===」を指します。

厳密等価演算子「===」は、左辺と右辺のオペランドが同じ型で、かつ、同じ値である場合にのみtrueを返します。

これは、型変換を行わずに比較が行われるため、値と型の両方が完全に一致する場合のみtrueと評価されます。

○厳密等価演算子の基本理解

例として、次のコードを考えてみましょう。

let num1: number = 5;
let str1: string = "5";

console.log(num1 === str1);  // 出力結果: false

このコードでは、数字の5と文字列の”5″を厳密等価演算子で比較しています。

結果はfalseとなります。

なぜなら、num1はnumber型で、str1はstring型であり、型が異なるためです。

同じ型の場合には、次のようになります。

let num2: number = 5;
let num3: number = 5;

console.log(num2 === num3);  // 出力結果: true

こちらのコードでは、num2とnum3はともにnumber型で、その値も5と同じです。

そのため、厳密等価演算子で比較した結果はtrueとなります。

しかし、次のような場合はどうでしょうか。

let obj1 = { id: 1 };
let obj2 = { id: 1 };

console.log(obj1 === obj2);  // 出力結果: false

obj1とobj2は、内容としては同じですが、異なるメモリの位置に存在する2つの異なるオブジェクトです。

そのため、厳密等価演算子で比較した場合、結果はfalseとなります。

●厳密等価演算子の使い方

TypeScriptでの等価性チェックには、主に2つの等価演算子が存在します。それは==(等価演算子)と===(厳密等価演算子)です。

この2つの違いを知ることは、TypeScriptやJavaScriptでのプログラミングにおいて、極めて重要です。

特にTypeScriptでの型の厳格性を活かすためには、===を積極的に活用することが推奨されます。

厳密等価演算子===は、左辺と右辺が同じ型であり、かつ、その値も同じである場合にのみtrueを返します。

一方で、==は型変換を伴う可能性があるため、厳格な比較が行えない場面も多々あります。

例を見て、具体的な動作の違いを把握しましょう。

○サンプルコード1:基本的な厳密等価演算の例

// 数字の比較
const num1 = 5;
const num2 = 5;
console.log(num1 === num2); // true

// 文字列の比較
const str1 = "hello";
const str2 = "hello";
console.log(str1 === str2); // true

// 数字と文字列の比較
const str3 = "5";
console.log(num1 === str3); // false

このコードでは、まず数字の比較を行っています。

num1num2は両方とも5という値を持っているので、trueが出力されます。

次に、文字列の比較を行っています。

str1str2も同じ文字列を持っているので、この場合もtrueが出力されます。

最後に、数字と文字列の比較を行っています。

num1は数字の5str3は文字列の”5″を持っています。厳密等価演算子は型の違いも考慮するため、この比較結果はfalseとなります。

○サンプルコード2:文字列と数値の厳密等価演算子

TypeScriptの中で、初心者が特に注意すべきトピックの一つが、文字列と数値の間での厳密等価演算子の挙動です。

JavaScriptとTypeScriptでの動作に違いがあり、それを理解しないと意図しない結果を招く可能性があります。

JavaScriptにおいて、== は等価演算子として機能し、型変換を伴いながら比較します。

そのため、文字列”5″と数値5をこの演算子で比較すると、真と評価されます。

一方で、TypeScriptやJavaScriptで === という演算子を使用すると、これは厳密等価演算子となり、型変換を行わずに比較するため、文字列”5″と数値5は偽と評価されます。

このコードでは、文字列と数値の等価演算と厳密等価演算の結果を出力しています。

この例では、== 演算子を使って文字列と数値を比較している一方で、=== 演算子を使って同じ値を厳密に比較しています。

let strValue: string = "5";
let numValue: number = 5;

console.log(strValue == numValue);  // 真: 型変換が行われて比較される
console.log(strValue === numValue); // 偽: 型変換なしで比較される

このコードを実行すると、コンソールには次のように出力されます。

最初のconsole.logtrueを表示します。

これは、== 演算子が型変換を行って比較するためです。

文字列の”5″は数値の5に変換され、等しいと評価されます。

一方、次のconsole.logfalseを表示します。

これは、=== 演算子が型変換を行わずに比較するため、文字列と数値は等しくないと評価されるからです。

○サンプルコード3:オブジェクトと厳密等価演算子

TypeScriptを学び進める中で、プリミティブなデータ型の比較だけでなく、オブジェクトの比較も頻繁に行うことになります。

ここでは、オブジェクトを対象とした厳密等価演算子(===)の振る舞いと、それをうまく活用する方法について詳しく解説します。

□オブジェクトの基本的な比較

このコードでは、オブジェクトの基本的な比較を表しています。

const objA = { key: 'value' };
const objB = { key: 'value' };
const objC = objA;

console.log(objA === objB); // false
console.log(objA === objC); // true

この例では、objAobjBは同じプロパティと値を持つオブジェクトを参照していますが、異なるメモリアドレスに存在するため、objA === objBfalseを返します。

一方で、objCobjAの参照をコピーしているので、objA === objCtrueとなります。

□オブジェクトのネストした比較

次に、ネストされたオブジェクトの比較を見てみましょう。

const nestedObjA = { inner: { key: 'value' } };
const nestedObjB = { inner: { key: 'value' } };

console.log(nestedObjA === nestedObjB); // false
console.log(nestedObjA.inner === nestedObjB.inner); // false

この例では、nestedObjAnestedObjBは同じ構造のオブジェクトを持っていますが、===を使った比較ではfalseが返されます。

これは、内部のオブジェクトも異なるメモリアドレスを持っているためです。

オブジェクトの比較では、参照先のアドレスが同じかどうかで判定されるため、中身が同じでも異なるオブジェクトとして認識されるのです。

オブジェクトの比較を行う際、中身が同じかどうかを確認するためのユーティリティ関数などを用いることで、期待する結果を得ることができます。

しかし、基本的に===演算子だけでは、ネストされたオブジェクトの中身の比較は難しい点に注意が必要です。

このコードの中で、nestedObjAnestedObjBの比較、そしてnestedObjA.innernestedObjB.innerの比較を行った結果、どちらもfalseが返されることがわかります。

これは、参照型のオブジェクトが異なるメモリアドレスに配置されているためです。

●厳密等価演算子の応用例

TypeScriptの厳密等価演算子(===)は、初心者でも理解しやすく、高度なプログラミングの際にも役立つツールとなっています。

基本的な使い方や文字列、数値の比較を学んだ後、今回はさらに高度な使用例に焦点を当て、関数内での利用方法について詳しく解説していきます。

○サンプルコード4:関数内での厳密等価演算子の使用例

関数の中での厳密等価演算子の利用は、引数や戻り値のチェック、内部の変数の状態チェックなど、さまざまなシーンで非常に役立ちます。

下記のサンプルコードでは、関数内で厳密等価演算子を使用して、引数が特定の値と一致するかどうかをチェックしています。

// 関数定義
function checkValue(value: number): string {
    // 引数が10と厳密等価かどうかを確認
    if (value === 10) {
        return '引数は10です。';
    } else {
        return '引数は10ではありません。';
    }
}

// 関数呼び出し
const result = checkValue(10);
console.log(result);  // 出力結果は「引数は10です。」となります。

このコードでは、関数checkValueを定義しており、この関数の引数valueが10と厳密等価かどうかを確認しています。

引数が10と一致すれば「引数は10です。」という文字列を、そうでなければ「引数は10ではありません。」という文字列を返します。

関数を呼び出す際には、10を引数として与えているので、期待通り「引数は10です。」という文字列が返され、これがコンソールに出力されます。

このように、関数内での厳密等価演算子の使用は、特定の条件下での処理を実装する際や、バリデーションのチェック、特定の値との比較など、さまざまな場面で役立つことがわかります。

関数の引数や内部の変数の状態に応じて、異なる処理や出力をしたい場合には、厳密等価演算子を活用して条件分岐を行うと良いでしょう。

○サンプルコード5:クラスとインスタンスの比較

TypeScriptの中で厳密等価演算子を使用する際の一つの興味深いトピックは、クラスとインスタンス間の比較です。

この項目では、クラスとインスタンスを厳密等価演算子でどのように比較するか、またその背後の仕組みを詳しく説明していきます。

まず、TypeScriptやJavaScriptのクラスについて簡単に説明します。

クラスはオブジェクト指向プログラミングの基本的な概念で、データとそのデータを操作するメソッドをひとまとめにしたものです。

TypeScriptのクラスは、JavaScriptのES6以降で導入されたクラス構文を拡張しています。

クラス自体はあくまで設計図のようなものです。

この設計図を元に実際にデータを持つオブジェクトを作成することをインスタンス化と言います。

この生成されたオブジェクトをインスタンスと呼びます。

では、TypeScriptにおけるクラスとインスタンスの比較について見ていきましょう。

class Animal {
  constructor(public name: string) {}
}

const dog1 = new Animal("Dog");
const dog2 = new Animal("Dog");

console.log(dog1 === dog2);  // false
console.log(dog1.name === dog2.name);  // true

このコードでは、Animalというクラスを定義しています。

そして、そのクラスからdog1dog2という二つのインスタンスを生成しています。

最後の部分で、二つのインスタンスとそのプロパティnameを比較しています。

結果として、dog1dog2は異なるインスタンスなので、falseが返ります。

しかし、nameプロパティの値はどちらも”Dog”と同じなので、trueが返ります。

ここからわかることは、クラスのインスタンスは、そのインスタンスが持つプロパティの値が同じであっても、異なるメモリアドレスに保存されるため、厳密等価演算子で比較するとfalseが返されるということです。

しかし、プロパティの値自体を比較する場合、その値が同じであればtrueが返されます。

○サンプルコード6:配列の要素比較

TypeScriptを利用していると、配列の要素を比較する場面も少なくありません。

しかし、配列の比較は一筋縄ではいきません。

配列そのものを比較する場合、参照が一致するかどうかが確認されるため、内容が同じであっても参照が異なれば、等しくありません。

そこで、この章では配列の要素を一つずつ厳密等価演算子を使用して比較する方法を解説します。

配列の要素を一つずつ厳密等価演算子を使って比較するサンプルコードを紹介します。

// 二つの配列の要素を比較する関数
function areArraysEqual(arr1: any[], arr2: any[]): boolean {
    // まず、配列の長さを比較する
    if (arr1.length !== arr2.length) {
        return false;
    }

    // 配列の要素を一つずつ厳密等価演算子で比較する
    for (let i = 0; i < arr1.length; i++) {
        if (arr1[i] !== arr2[i]) {
            return false;
        }
    }
    return true;
}

// 使用例
const array1 = [1, 2, 3];
const array2 = [1, 2, 3];
const array3 = [1, 2, 4];

console.log(areArraysEqual(array1, array2));  // true
console.log(areArraysEqual(array1, array3));  // false

このコードでは、まず配列の長さを比較しています。

その理由は、長さが異なれば明らかに配列の内容も異なるためです。

次に、配列の各要素を厳密等価演算子を使用して比較しています。

すべての要素が等しい場合のみ、trueを返すようになっています。

実際に上記のコードを実行すると、areArraysEqual(array1, array2)trueと評価されます。

これはarray1array2のすべての要素が等しいためです。

一方、areArraysEqual(array1, array3)falseと評価されます。

これは、array1array3の3番目の要素が異なるためです。

しかし、この方法には注意が必要です。

例えば、配列の中に別の配列やオブジェクトが含まれている場合、上記の方法では正確な比較ができません。

そのような場合は、再帰的に比較を行うなどの方法を考慮する必要があります。

○サンプルコード7:TypeGuardとしての厳密等価演算子

TypeScriptの強力な型システムを十分に活用するためには、TypeGuardというテクニックを知ることが非常に有益です。

TypeGuardを使用すると、特定の条件下で変数の型を狭めることができます。

これにより、コンパイル時に安全なコードを書くのがさらに容易になります。

そして、厳密等価演算子を用いたTypeGuardは、初心者にとっても非常に役立つ知識となります。

この章では、厳密等価演算子を使用してTypeGuardを実装する方法を詳しく解説します。

function isString(value: unknown): value is string {
  return typeof value === 'string';
}

このコードでは、isStringという関数を定義しています。

この関数はunknown型の値を受け取り、その値がstring型であるかどうかを判定します。

value is stringという型アノテーションはTypeGuardを表しており、valuestring型であることをTypeScriptに通知します。

この例では、typeofオペレータを使用してvalueの型をチェックし、'string'と厳密に等しいかどうかを確認しています。

では、この関数をどのように使用するか見てみましょう。

function printLength(value: unknown) {
  if (isString(value)) {
    console.log(value.length);
  } else {
    console.log("値は文字列ではありません。");
  }
}

このコードでは、printLength関数を定義しています。

この関数はunknown型の値を受け取り、その値がstring型であるかどうかをisString関数でチェックします。

もしstring型であれば、その文字列の長さをコンソールに出力します。

そうでなければ、”値は文字列ではありません。”というメッセージを出力します。

例として、printLength("Hello, TypeScript!")を実行すると、17という数字が出力されます。

一方で、printLength(12345)を実行すると、”値は文字列ではありません。”というメッセージが出力されることになります。

●注意点と対処法

TypeScriptの厳密等価演算子を活用する際には、その特性や機能を十分に理解して使用することが必要です。

特に初心者の方には、注意すべきポイントや誤解しやすい部分が存在します。

ここでは、そのような注意点とそれを適切に対処する方法について詳しく解説します。

○nullとundefinedとの比較

nullundefinedは、多くのプログラミング言語に存在する特殊な値ですが、JavaScriptやTypeScriptではこれらの違いが非常に微妙です。

厳密等価演算子を使用する際、これらの値の比較には注意が必要です。

このコードでは、nullundefinedの比較をしています。

この例では、nullundefinedを厳密等価演算子で比較して、その結果を表示しています。

const value1 = null;
const value2 = undefined;

console.log(value1 === value2); // false

このサンプルコードの実行結果、nullundefinedは厳密等価演算子を使って比較すると、異なると判定されます。

しかし、等価演算子(==)を使うと、両者は同じと判定されるため、注意が必要です。

○NaNの取扱い

JavaScriptやTypeScriptでは、NaN(Not a Number)という特殊な値が存在します。この値は数値ではないことを表すために使用されます。

しかし、NaNの取り扱いは非常にユニークで、厳密等価演算子を使用した場合でも、自身と比較してもfalseとなります。

このコードでは、NaNを使っての比較を実施しています。

この例では、NaNNaNを厳密等価演算子で比較し、その結果を表示しています。

const num1 = NaN;
const num2 = NaN;

console.log(num1 === num2); // false

上記のサンプルコードを実行すると、NaN同士の比較結果はfalseとなります。

これはNaNの特性として知っておく必要があります。

○オブジェクトの参照比較

TypeScriptやJavaScriptでオブジェクトを比較する際に、内容が同じであっても、異なる参照を持っている場合、その二つのオブジェクトは異なると判定されます。

このコードでは、オブジェクトの参照比較に関して説明しています。

この例では、内容が同じである二つのオブジェクトを作成し、それらを厳密等価演算子で比較しています。

const obj1 = { key: "value" };
const obj2 = { key: "value" };

console.log(obj1 === obj2); // false

このサンプルコードの実行結果、内容が同じobj1obj2ですが、異なる参照を持っているため、厳密等価演算子による比較結果はfalseとなります。

オブジェクトの内容のみを比較する場合、独自の比較関数を作成するなどの対応が必要となります。

●カスタマイズ方法

TypeScriptの厳密等価演算子は非常に便利ですが、カスタム型や拡張メソッドを使用することで、さらに柔軟な比較を実現することが可能です。

ここでは、それらのカスタマイズ方法を詳しく解説します。

○カスタム型での厳密等価演算子の挙動

TypeScriptでは、ユーザーが独自の型を定義することができます。

このようなカスタム型を使用した場合、厳密等価演算子の挙動はどのようになるのでしょうか。

例として、次のようなPerson型を考えます。

type Person = {
    name: string;
    age: number;
};

このコードでは、Person型という新しい型を定義しています。

この例では、nameプロパティとageプロパティを持つオブジェクトをPerson型として扱います。

次に、2つのPerson型の変数を定義し、それらを厳密等価演算子で比較する例を考えます。

const person1: Person = { name: "田中", age: 25 };
const person2: Person = { name: "田中", age: 25 };

console.log(person1 === person2);  // false

この例では、person1person2という2つの変数がありますが、これらは異なるメモリ領域に存在するオブジェクトです。

そのため、厳密等価演算子で比較した場合、falseが出力されます。

ここで重要なのは、オブジェクトのプロパティの値が同じであっても、異なるメモリ領域に存在するオブジェクトは等しくないと判断される点です。

○拡張メソッドを使用した比較

厳密等価演算子による比較だけでなく、拡張メソッドを利用して独自の比較方法を定義することもできます。

これにより、特定のプロパティのみを比較する、あるいは独自の比較ロジックを適用することが可能となります。

例として、上述のPerson型の2つのインスタンスが、nameプロパティのみで等しいかどうかを判定するメソッドを考えます。

function isSamePersonByName(personA: Person, personB: Person): boolean {
    return personA.name === personB.name;
}

console.log(isSamePersonByName(person1, person2));  // true

このコードでは、isSamePersonByNameという関数を定義しています。

この関数は、2つのPerson型のオブジェクトを受け取り、nameプロパティが等しい場合にtrueを返します。

そのため、この関数を使用してperson1person2を比較すると、trueが出力されます。

まとめ

TypeScriptでのプログラミングを進める中で、厳密等価演算子は非常に重要な役割を果たしています。

本記事を通じて、その基本的な使い方や注意点、カスタマイズの方法などを解説してきました。

プログラミングは絶えず新しい技術や知識が更新される分野です。

常に学び続けることで、より高度なスキルや知識を身につけることができます。