TypeScript組み込み型の完全ガイド!21のコードで完全習得 – JPSM

TypeScript組み込み型の完全ガイド!21のコードで完全習得

TypeScript組み込み型のイラストと、サンプルコードを囲んだ画像TypeScript

 

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

このサービスは複数のSSPによる協力の下、運営されています。

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

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

また、理解しにくい説明や難しい問題に躓いても、JPSMがプログラミングの解説に特化してオリジナルにチューニングした画面右下のAIアシスタントに質問していだければ、特殊な問題でも指示に従い解決できるように作ってあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

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

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

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

はじめに

TypeScriptは、JavaScriptのスーパーセットとして人気を集めているプログラミング言語です。

その中でも組み込み型は、TypeScriptの魅力の一部と言っても過言ではありません。

この記事ではTypeScriptの組み込み型の基本から、使い方、注意点、カスタマイズ方法に至るまで、21の実践的なサンプルコードを通して徹底的に解説していきます。

初心者でも安心して学べるように、各サンプルコードには詳しい説明と実行後の結果も紹介しています。

それでは、TypeScriptの組み込み型を一緒に学んでいきましょう!

●TypeScript組み込み型の基本

TypeScriptは、JavaScriptに型システムを導入することで、大規模なプロジェクトやチームでの開発を助けるプログラミング言語として知られています。

その中核となるのが、組み込み型です。

TypeScriptが提供する組み込み型は、プログラミング時に変数や関数の返り値などの型を明確にするためのもので、コードの安全性や予測可能性を高めます。

○何故組み込み型を知る必要があるのか

TypeScriptを使って開発を行う上で、組み込み型の知識は必須です。

これにより、開発者はコードの間違いを早期に検出したり、意図しない動作を予防することができます。

また、チームメンバー間でのコミュニケーションも円滑に行えるようになります。

○組み込み型の一覧と概要

組み込み型とは、TypeScriptに標準で用意されている型のことを指します。

主な組み込み型には、any, unknown, number, string, boolean, object, array などがあります。

それぞれの型には独自の特性や用途があり、適切な場面で使用することで、コードの品質や安全性を高めることができます。

このコードでは、TypeScriptで変数を定義し、それぞれの組み込み型を指定しています。

この例では、各変数に異なる組み込み型を指定し、それらの変数の型を表示しています。

let anyType: any = 'これはany型です';
let unknownType: unknown = 'これはunknown型です';
let numType: number = 123;
let strType: string = 'これはstring型です';
let boolType: boolean = true;

console.log(typeof anyType);      // 'string'
console.log(typeof unknownType);  // 'string'
console.log(typeof numType);      // 'number'
console.log(typeof strType);      // 'string'
console.log(typeof boolType);     // 'boolean'

上記のサンプルコードを実行すると、コンソールにそれぞれの変数の型が表示されます。

ここでのポイントは、any型とunknown型はどんな型も受け入れることができるという特性を持つこと、そしてそれぞれの変数に適切な型を指定することで、その変数がどんな値を持つべきかが明確になることです。

●TypeScript組み込み型の使い方

TypeScriptの組み込み型を習得することで、プログラムの堅牢性や開発効率を大幅に向上させることができます。

特に、TypeScriptの強力な型機能は、バグの発生を事前に防ぐ上で重要な役割を果たします。

ここでは、TypeScriptの組み込み型の使い方とその特徴について、実践的なサンプルコードとともに徹底的に解説します。

初心者の方でもわかりやすく、具体的なコードの例を交えながら説明していきます。

○サンプルコード1:any型の使用方法

このコードでは、any型を使って、TypeScriptで任意の型を持つ変数を定義するコードを表しています。

この例では、任意の型の値を持つ変数dataを定義し、異なる型の値を代入しています。

let data: any;
data = "Hello, TypeScript!";
data = 42;
data = true;

上のコードでは、dataという変数に文字列、数値、真偽値を順に代入しています。

any型を使用することで、どんな型の値でも代入することが可能になります。

しかし、このany型を過度に使用すると、型の恩恵を受けることができなくなるため、必要な場面でのみ使用することをおすすめします。

○サンプルコード2:unknown型を活用する

このコードでは、unknown型を使って、TypeScriptで安全に不明な型の変数を扱う方法を表しています。

この例では、unknown型の値を持つ変数valueを定義し、その後で型ガードを使用して安全にアクセスしています。

let value: unknown;
value = "TypeScript is awesome!";
if (typeof value === "string") {
    console.log(value.toUpperCase());
}

上記のコードでは、valueに文字列を代入した後、typeofを使った型ガードで、valueが文字列である場合にのみtoUpperCaseメソッドを呼び出しています。

このように、unknown型を使用すると、型安全に不明な型のデータを扱うことができます。

○サンプルコード3:number型とその操作

このコードでは、number型を使って、数値の変数を操作するコードを表しています。

この例では、基本的な算術操作とMathオブジェクトを使用した数学的な操作を行っています。

let num1: number = 10;
let num2: number = 20;
let result: number;

// 基本的な算術操作
result = num1 + num2;
console.log(`Addition: ${result}`);

result = num1 - num2;
console.log(`Subtraction: ${result}`);

// Mathオブジェクトを使用した操作
result = Math.max(num1, num2);
console.log(`Max value: ${result}`);

上記のコードでは、num1とnum2という2つの数値を用いて、加算、減算を行っています。

また、Mathオブジェクトのmaxメソッドを用いて、2つの数値のうち大きい方を取得しています。

○サンプルコード4:string型の活用テクニック

このコードでは、string型を使って、文字列の操作を行うコードを表しています。

この例では、文字列の連結や部分文字列の取得、大文字・小文字への変換を行っています。

let str1: string = "Hello";
let str2: string = "TypeScript";

// 文字列の連結
let combined: string = str1 + " " + str2;
console.log(combined);

// 部分文字列の取得
let subString: string = str2.substring(0, 4);
console.log(subString);

// 大文字・小文字への変換
console.log(str1.toUpperCase());
console.log(str2.toLowerCase());

このコードでは、str1とstr2という2つの文字列を使って、さまざまな文字列の操作を行っています。

特に、+演算子を使用した文字列の連結や、substringメソッドを使用した部分文字列の取得、toUpperCaseやtoLowerCaseメソッドを用いた文字列の変換が行われています。

○サンプルコード5:boolean型での条件分岐

TypeScriptでのプログラム作成において、条件分岐は必須のテクニックとして多用されます。

boolean型は真偽値を表す型として、条件分岐の核心となる部分です。

このコードでは、boolean型を使って条件分岐を実施する方法を表しています。

この例では、ある条件が成立するかどうかを判定し、それに基づいて処理を分岐させる方法を表しています。

// boolean型の変数を宣言
let isRaining: boolean = true;

// if文での条件分岐
if (isRaining) {
    console.log("傘を持って出かけましょう。");
} else {
    console.log("傘は必要ありません。");
}

このサンプルコードでは、isRainingというboolean型の変数を用いて、雨が降っているかどうかを表現しています。

if文を利用して、isRainingがtrueの場合とfalseの場合で、表示するメッセージを変更しています。

このコードを実際に実行すると、変数isRainingがtrueのため、”傘を持って出かけましょう。”というメッセージがコンソールに表示されます。

応用例として、boolean型の変数を関数の返り値として活用することも考えられます。

ある数値が10以上かどうかを判定する関数の例を紹介します。

function isOverTen(num: number): boolean {
    return num >= 10;
}

// 使用例
const result = isOverTen(15);
if (result) {
    console.log("数値は10以上です。");
} else {
    console.log("数値は10未満です。");
}

この例では、isOverTen関数がboolean型の値を返すことで、数値が10以上かどうかを簡単に判定しています。

関数の返り値を直接if文の条件として利用することで、コードがシンプルになり、読みやすくなります。

○サンプルコード6:object型の基本

TypeScriptは、JavaScriptの上に型情報を追加するスーパーセットとして知られています。組み込み型の中で、非常に一般的に使用されるのがobject型です。

JavaScriptの世界におけるオブジェクトは、キーと値のペアの集まりで、多くの情報をまとめるのに便利です。

TypeScriptのobject型もその特性を継承しており、より安全にオブジェクトを扱うことができます。

このコードではobject型を使って基本的なオブジェクトを定義するコードを表しています。

この例では、人物の情報を保持するオブジェクトを作成しています。

// Person型としてオブジェクトの構造を定義
type Person = {
    name: string;
    age: number;
};

// Person型のオブジェクトを作成
const taro: Person = {
    name: "太郎",
    age: 25
};

console.log(taro.name);  // 太郎
console.log(taro.age);   // 25

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

この型はnameという文字列型のキーと、ageという数値型のキーを持つオブジェクトを表しています。

次に、このPerson型を使用してtaroという変数を定義し、オブジェクトの値を割り当てています。

最後に、そのオブジェクトの各キーの値をconsole.logを使用して出力しています。

上のコードを実行すると、ターミナルには次のような出力が表示されるでしょう。

太郎と25という文字列が表示されます。これにより、Person型のオブジェクトを正しく作成し、それを出力することができたことが確認できます。

しかし、object型の力はこれだけではありません。

例えば、オブジェクト内のキーを動的に変更したり、オブジェクトにメソッドを追加することもできます。

type FlexiblePerson = {
    name: string;
    age: number;
    greet?: () => string;  // オプショナルなメソッド
};

const hanako: FlexiblePerson = {
    name: "花子",
    age: 23,
    greet: function() {
        return `こんにちは、私は${this.name}です。`;
    }
};

console.log(hanako.greet());  // こんにちは、私は花子です。

こちらのコードでは、greetというオプショナルなメソッドを持つ新しい型FlexiblePersonを定義しています。

この型のオブジェクトを作成する際、greetメソッドは必須ではありませんが、提供された場合はそのメソッドを使って挨拶のメッセージを出力することができます。

上記のコードを実行すると、ターミナルには「こんにちは、私は花子です。」というメッセージが表示されます。

このように、TypeScriptのobject型は、JavaScriptの柔軟性を保ちつつ、型の安全性も確保することができるのが特徴です。

○サンプルコード7:array型の活用方法

TypeScriptでは、配列を効率的に扱うためのarray型が提供されています。

このarray型は、多くのプログラミング言語において基本的なデータ構造として利用されている配列を型として扱うことを可能にします。

配列とは、同じ型の要素を順番に格納するためのコレクションです。

TypeScriptのarray型を使って、数値や文字列などの異なるデータ型の要素を順番に格納することができます。

このコードでは、数値の配列を作成して、その要素を操作する基本的な方法を表しています。

この例では、数値の配列を定義し、要素の追加、参照、変更を行っています。

// 数値の配列を定義
let numbers: number[] = [1, 2, 3, 4, 5];

// 配列の要素を参照
let firstNumber = numbers[0]; // 1

// 配列の要素を変更
numbers[2] = 8;  // numbersは[1, 2, 8, 4, 5]になります。

// 配列の末尾に要素を追加
numbers.push(6);  // numbersは[1, 2, 8, 4, 5, 6]になります。

上記のコードを実際に実行すると、numbers配列は最初に[1, 2, 3, 4, 5]として定義され、その後、要素の変更や追加が行われることで配列の中身が変化します。

TypeScriptのarray型を利用する際の注意点として、配列に格納する要素のデータ型はすべて同じでなければならないという点が挙げられます。

例えば、数値と文字列の混在した配列を作成しようとすると、TypeScriptは型エラーを発生させます。

また、array型をカスタマイズする応用例として、配列の要素のデータ型をカスタム型で定義することが考えられます。

// カスタム型の定義
type User = {
    id: number;
    name: string;
};

// カスタム型の配列を定義
let users: User[] = [
    { id: 1, name: "Taro" },
    { id: 2, name: "Hanako" }
];

// 配列の要素を参照
let firstUser = users[0]; // { id: 1, name: "Taro" }

このコードでは、ユーザー情報を表すカスタム型Userを定義して、その型の要素を持つ配列usersを作成しています。

このように、カスタム型を利用して、複雑なデータ構造の配列を効率的に扱うことができます。

○サンプルコード8:タプル型で複数の型情報を保持

TypeScriptにおけるタプル型は、異なる型の情報を同一の配列内で管理する強力なツールとなります。

従来の配列型では、同じ型の要素しか持てなかったのですが、タプル型を使うと、例えば、文字列と数値を1つの配列に格納することが可能です。

このコードではタプル型を使用して、文字列と数値、そして真偽値を同一の配列内で管理する方法を表しています。

この例では、人物の名前、年齢、そして彼が学生であるかどうかの情報を1つのタプルに格納しています。

let person: [string, number, boolean] = ["山田太郎", 25, true];

上のコードのperson変数は、最初の要素が文字列、次の要素が数値、そして最後の要素が真偽値となっています。

このタプルを利用することで、次のように各要素にアクセスすることができます。

let name = person[0]; // "山田太郎"
let age = person[1]; // 25
let isStudent = person[2]; // true

タプル型の大きな利点は、異なる型のデータを一つのまとまりとして扱える点です。

これにより、関連する情報をグループ化して効率的に管理することが可能となります。

しかしながら、タプルの大きな制約として、一度定義した要素の数や型は変更できないことが挙げられます。

例えば、次のようなコードはエラーとなります。

person[3] = "東京"; // エラー:Index '3' is out of range.

また、異なる型を代入しようとすると、それもエラーとなります。

person[0] = 123; // エラー:Type '123' is not assignable to type 'string'.

このような制約のため、タプルを使用する際には注意が必要です。

適切な場面での利用を心がけ、その強力な特性を最大限に活用しましょう。

タプルを使用する上での一例として、関数が複数の異なる型の値を一度に返したいときなどにも活用されます。

次のような関数は、名前と年齢を返す関数の例です。

function getUserInfo(): [string, number] {
    return ["鈴木一郎", 30];
}

let [userName, userAge] = getUserInfo();

上記のように、関数がタプルを返す場合、戻り値を分割代入を使用して各変数に格納することが可能です。

○サンプルコード9:enumで定数を管理

TypeScriptは、JavaScriptには存在しない特別な型のセットを持っています。

その中でも「enum型」は、一連の定数を管理するための有効な手段として広く利用されています。

enumは「列挙型」とも呼ばれ、予め定められた選択肢からのみ選ばれる値を持つ変数を定義する際に非常に役立ちます。

例えば、曜日や月、色など、限定された選択肢からのみ選ぶことができる項目をプログラム内で管理する際にはenumを使うと効率的です。

このコードでは、日本の四季を示すSeasonというenumを作成しています。

この例では、春、夏、秋、冬の四つの季節を定数として持っており、それぞれの季節には0から3までの数値が自動的に割り当てられています。

// Seasonというenumを定義
enum Season {
    Spring,  // 0
    Summer,  // 1
    Autumn,  // 2
    Winter   // 3
}

// enumを使用する例
let myFavoriteSeason: Season = Season.Autumn;
console.log(myFavoriteSeason);  // 2 (Autumnには2が割り当てられている)
console.log(Season[myFavoriteSeason]); // "Autumn"

上記のコードを実行すると、myFavoriteSeason変数はSeason.Autumnに対応する数値、すなわち2を取得します。

また、enumの値から名前を逆引きすることも可能です。

そのため、Season[myFavoriteSeason]とすることで”Autumn”という文字列が取得できます。

enumは数値だけでなく、文字列も割り当てることが可能です。

下記のコードでは、それぞれの季節に対応する風物詩を文字列として割り当てています。

enum SeasonWithPoetry {
    Spring = "桜の花",
    Summer = "海の音",
    Autumn = "紅葉狩り",
    Winter = "雪の結晶"
}

let poetry: string = SeasonWithPoetry.Spring;
console.log(poetry);  // "桜の花"

この場合、SeasonWithPoetry.Springは”桜の花”という文字列を返します。

enumの力強さは、明確に名前がつけられた一連の値を持つことができる点にあります。

これによってコードの読みやすさが向上し、バグの発生リスクも低減します。

○サンプルコード10:void型の適切な使用時

TypeScriptの組み込み型にはさまざまなものがありますが、今回は「void型」という特殊な型について深く探ります。

void型は、関数が特定の値を返さないことを示すために使用されます。

JavaScriptにはundefinedという値が存在しますが、TypeScriptではこのvoid型を使って、関数が明示的に値を返さないことを表すことができるのです。

このコードでは、void型を使って関数が値を返さないことを表しています。

この例では、関数内でconsole.logを使ってメッセージを表示しているだけで、return文がありません。

function showGreeting(): void {
    console.log("こんにちは、TypeScript!");
}

上の関数を呼び出しても、何も返ってこないことがわかります。

このように、void型は関数が何も返さないことを示すのに役立ちます。

応用例として、次のような関数も考えられます。

function processFile(fileName: string, callback: (message: string) => void): void {
    console.log(`ファイル${fileName}を処理中...`);
    // ファイルの処理が終わったらコールバック関数を呼ぶ
    callback(`ファイル${fileName}の処理が完了しました。`);
}

上の関数は、ファイルを処理するという仮の関数です。

第2引数のcallbackは、ファイルの処理が完了した後に呼び出す関数を指定します。

そして、このコールバック関数は文字列を引数に取り、voidを返す関数でなければなりません。

実際に上の関数を使用すると次のようになります。

processFile('sample.txt', message => {
    console.log(message);
});

このコードを実行すると、「ファイルsample.txtを処理中…」の後に「ファイルsample.txtの処理が完了しました。」と表示されます。

コールバック関数内ではmessageの内容をログとして出力しています。

注意点として、void型を使った関数にreturn文を書くと、TypeScriptコンパイラがエラーを出すことがあります。

void型は「何も返さない」という意味なので、return文を使用する場合は返す値がないことを確認しましょう。

○サンプルコード11:nullとundefinedの違い

JavaScriptとその上に構築されたTypeScriptは、nullundefinedの2つの特別な値を持っています。

これらは似ているように見えますが、異なる目的と使用方法を持っています。

ここでは、それぞれの違いを明確にし、実践的なTypeScriptコードを使ってその特性を探っていきます。

このコードでは、nullundefinedの違いを表す基本的な例を表しています。

この例では、変数にそれぞれの値を代入し、その型注釈としての振る舞いを表しています。

let nullVariable: null = null;
let undefinedVariable: undefined = undefined;

let stringOrNull: string | null = 'Hello';
stringOrNull = null;

let numberOrUndefined: number | undefined;
numberOrUndefined = 42;
numberOrUndefined = undefined;

上記のコードは、まずnullundefinedのみを持つ変数を定義しています。

それぞれの型としてnullundefinedを使用することで、その変数が持てる値はそれぞれの特別な値だけとなります。

次に、合併型を用いて、stringまたはnullnumberまたはundefinedといった値を持つ変数を定義しています。

nullはある値が存在しないことを表すときに使われ、オブジェクトや関数、その他の型の値が存在しないことを示す場面で使われることが多いです。

一方、undefinedは変数がまだ値を持っていない、またはその値が設定されていないことを表す場面でよく使われます。

上記のコードの実用的な結果としては、stringOrNull変数は文字列を持つことができ、後でnullに設定することができます。

一方、numberOrUndefined変数は数値を持つか、未定義の状態(undefined)であるかのどちらかとなります。

注意点として、TypeScriptの設定でstrictNullChecksオプションがtrueに設定されている場合、nullundefinedは他の型に自動的に代入されないため、明示的に合併型を使用する必要があります。

応用例として、関数の戻り値が特定の型かnullまたはundefinedであることを表す場合、次のようなコードを考えることができます。

function findUserById(id: number): User | null {
    const user = database.findById(id);
    if (user) {
        return user;
    } else {
        return null;
    }
}

このコードでは、User型のオブジェクトまたはnullを返す関数を表しています。

この例では、データベースからユーザーを検索し、ユーザーが見つかった場合はそのオブジェクトを、見つからなかった場合はnullを返しています。

○サンプルコード12:never型で到達不可能なコード

TypeScriptは、型システムの中で非常にユニークな型、never型を持っています。

この型は、関数が絶対に返さないことを表すためや、エラーを投げることのみを表すために使用されます。

このコードではnever型を使って、関数が絶対に値を返さないことを表しています。

この例では、generateError関数を使って、エラーメッセージを投げ、戻り値としてnever型を指定しています。

function generateError(message: string, code: number): never {
    // エラーメッセージをスロー
    throw { message: message, errorCode: code };
}

// この関数はエラーを投げるので、正常に戻ることはない
generateError("エラーが発生しました", 500);

このgenerateError関数は、どんな状況でもエラーをスローします。

そのため、この関数の後にコードが存在しても、そのコードは実行されることはありません。

関数の戻り値としてnever型を指定することで、関数が絶対に値を返さないことをTypeScriptに伝えることができます。

上記のコードを実行すると、指定したエラーメッセージとエラーコードを持ったオブジェクトをスローします。

これは、通常の実行フローが中断され、即座にエラーハンドリングメカニズムに移行することを意味します。

never型は、コードのある部分が絶対に到達しないことを示す際に非常に有用です。

しかし、不適切に使用すると、予期せぬエラーや不具合の原因となる可能性があります。

そのため、never型の使用場面と目的をしっかり理解してから利用することが重要です。

また、never型は、制御フローの分析においても役立ちます。

例えば、全てのケースを処理するswitch文を書く際に、全てのケースを処理した後のデフォルトのケースでエラーをスローすることで、新たなケースが追加された際の漏れを防ぐことができます。

type Fruit = "apple" | "banana" | "orange";

function getFruitPrice(fruit: Fruit): number {
    switch (fruit) {
        case "apple":
            return 100;
        case "banana":
            return 80;
        case "orange":
            return 90;
        default:
            const _exhaustiveCheck: never = fruit;
            return _exhaustiveCheck;
    }
}

上記のコードでは、Fruit型に新たなフルーツが追加された場合、switch文にそのケースが追加されていなければ、コンパイルエラーが発生します。

これにより、未処理のケースの漏れを事前に検知することができます。

○サンプルコード13:型アサーションの活用

TypeScriptでは、開発者が変数の型を特定の型だと明示することができる機能を「型アサーション」として提供しています。

型アサーションは、型が確定している場面であるにも関わらず、TypeScriptの型推論によって望んだ型が推論されない場面で使用します。

特に、JavaScriptのライブラリをTypeScriptで使用する場面などで役立ちます。

このコードでは、型アサーションの基本的な使い方を示すサンプルコードを表しています。

この例では、any型の変数をstring型として扱い、その後に文字列のメソッドを使用しています。

let unknownType: any = "Hello TypeScript!";
let assertString: string = unknownType as string;

console.log(assertString.toUpperCase());

上記のコードでは、unknownTypeという変数はany型として宣言されていますが、その中身は文字列の”Hello TypeScript!”です。

asキーワードを使用して、この変数をstring型として扱いたい場合、unknownType as stringという型アサーションを使って型を強制的に変更することができます。

この変換に成功すると、assertString変数はstring型として扱われるため、toUpperCaseなどの文字列のメソッドを使用することができます。

このサンプルコードを実行すると、コンソールには”HELLO TYPESCRIPT!”という大文字に変換された文字列が表示されるでしょう。

ただし、型アサーションは開発者が型を正確に知っている場合にのみ使用するべきです。

誤った型アサーションを使用すると、実行時にエラーが発生する可能性があります。

また、型アサーションは、基本的な型だけでなく、複雑なオブジェクトに対しても使用することができます。

下記のサンプルコードでは、オブジェクトに対して型アサーションを行い、そのプロパティにアクセスしています。

let obj: any = {
    name: "Taro",
    age: 30
};

let person: { name: string, age: number } = obj as { name: string, age: number };

console.log(`名前は${person.name}、年齢は${person.age}歳です。`);

このコードでは、obj変数にany型のオブジェクトが代入されています。

その後、このオブジェクトを特定の型として扱いたい場合、asキーワードを使用して型アサーションを行います。

成功すると、person変数を通じてオブジェクトのプロパティにアクセスすることができます。

このサンプルコードを実行すると、コンソールには”名前はTaro、年齢は30歳です。”という文字列が表示されるでしょう。

○サンプルコード14:交差型で複数の型を結合

TypeScriptには多くの組み込み型が存在していますが、その中でも特に便利な型として「交差型」があります。

この交差型は、複数の型情報を一つに結合する際に使用します。

オブジェクトの型情報を複数組み合わせたい場合や、既存の型に新たな情報を付与する際など、多岐にわたる場面で役立ちます。

このコードでは交差型を使って、2つのオブジェクトの型情報を結合するコードを表しています。

この例では、Person 型と Job 型を組み合わせて、新しい Employee 型を作成しています。

// Person型を定義
type Person = {
    name: string;
    age: number;
};

// Job型を定義
type Job = {
    jobTitle: string;
    salary: number;
};

// 交差型を使用して、Person型とJob型を結合
type Employee = Person & Job;

// Employee型のオブジェクトを作成
const employee: Employee = {
    name: "田中太郎",
    age: 25,
    jobTitle: "エンジニア",
    salary: 500000
};

console.log(employee);

上記のコードを実行すると、employee オブジェクトの内容がコンソールに出力されます。

このオブジェクトはPersonJobの両方の属性を持っています。

これにより、田中太郎という名前の25歳のエンジニアが、月給500,000円で働いているという情報を持つemployeeオブジェクトが生成されました。

これにより、複数の型情報を一つのオブジェクトで扱うことが可能になります。

注意点として、交差型を使用する際には、型が重複する場合、型が矛盾しているとエラーが発生します。

例えば、次のようなケースです。

type A = {
    name: string;
};

type B = {
    name: number;
};

// エラーが発生する
type C = A & B;

上記の例では、AB の両方で name プロパティが定義されていますが、その型が異なるため、交差型で結合するとエラーが発生します。

また、交差型は、複数の型を結合するだけでなく、既存の型に新たな属性を追加する際にも使用できます。

type Base = {
    id: number;
    createdAt: Date;
};

type ExtendedInfo = Base & {
    description: string;
};

const info: ExtendedInfo = {
    id: 1,
    createdAt: new Date(),
    description: "この情報はExtendedInfo型です。"
};

console.log(info);

このように、既存のBase型に新しい属性descriptionを追加したExtendedInfo型を定義し、それを使用してオブジェクトを作成することができます。

○サンプルコード15:ユニオン型で複数の型を一つにまとめる

TypeScriptは、ユーザが複数の型を持つ変数を扱いたいときのために、ユニオン型という機能を提供しています。

このコードでは、ユニオン型を使って複数の型の値を持つことができる変数を作成する方法を表しています。

この例では、文字列または数値を持つことができる変数を定義して、その変数に異なる型の値を代入しています。

// ユニオン型の定義
let value: string | number;

// 文字列を代入
value = "TypeScript";
console.log(value); // 出力: TypeScript

// 数値を代入
value = 42;
console.log(value); // 出力: 42

このコードの中で、value変数はstring | numberという型を持っているため、文字列や数値のどちらの型の値も代入することができます。

最初に文字列”TypeScript”を代入した後、次に数値42を代入しています。

これにより、コードの中で変数の型が動的に変わることが確認できます。

この特性は、関数の返り値や関数の引数など、さまざまな場面で非常に役立ちます。

例えば、エラーが発生した場合にエラーメッセージの文字列を、成功した場合には実際の値を返す関数を考えると、ユニオン型はその返り値として非常に適しています。

応用例として、ユーザー情報のIDを取得する関数を考えてみましょう。

この関数は、IDを取得できた場合にはそのID(数値)を返し、取得できなかった場合にはエラーメッセージ(文字列)を返すとします。

function getUserID(): string | number {
    // 仮のユーザーIDを取得するロジック
    const userID = Math.random() > 0.5 ? 12345 : "ユーザーIDを取得できませんでした。";
    return userID;
}

const result = getUserID();
if (typeof result === "number") {
    console.log(`ユーザーID: ${result}`);
} else {
    console.log(`エラー: ${result}`);
}

この例では、getUserID関数はユニオン型string | numberを返しています。

関数の内部でランダムにIDを取得するロジックをシミュレートしています。

返り値が数値の場合と文字列の場合とで、処理を分岐しています。

ユニオン型は、TypeScriptの強力な機能の一つです。しかし、使い方には注意が必要です。

特に、ユニオン型の変数を使用する際には、その変数の現在の型を正確に判別する必要があります。

このため、型ガードなどの技術を駆使して、安全にコードを書くことが求められます。

○サンプルコード16:リテラル型で具体的な値の型を定義

TypeScriptは、静的な型を持つため、特定の具体的な値のみを取る変数を定義するための「リテラル型」という強力な機能があります。

これは、特定の文字列や数値、または真偽値を指定して、それに制約を持つ変数を定義する際に非常に有効です。

このコードではリテラル型を使って具体的な値の型を定義するコードを表しています。

この例では、特定の文字列と数値を持つリテラル型を定義し、それに基づいて変数を作成しています。

// 文字列リテラル型の定義
type Day = 'Monday' | 'Tuesday' | 'Wednesday';

// Day型の変数を定義
let today: Day;
today = 'Monday';  // OK
// today = 'Thursday';  // エラー: Type '"Thursday"' is not assignable to type 'Day'.

// 数値リテラル型の定義
type DiceValue = 1 | 2 | 3 | 4 | 5 | 6;

// DiceValue型の変数を定義
let dice: DiceValue;
dice = 3;  // OK
// dice = 7;  // エラー: Type '7' is not assignable to type 'DiceValue'.

上のコードでDay型は、’Monday’, ‘Tuesday’, ‘Wednesday’の3つの文字列のいずれかしか受け付けないということを表しています。

同様に、DiceValue型は1から6までの数値しか受け入れません。

このように、リテラル型を使用すると、変数が取るべき具体的な値を厳格に制限することができます。

このような厳格な制約は、誤った値が代入されることを予防し、コードの品質と安全性を向上させます。

このコードを実際にTypeScriptで実行すると、上記の通りtodaydiceにリテラル型で指定されていない値を代入しようとするとエラーが発生します。

また、リテラル型は、関数の引数など、特定の値のみを受け入れたい場面で非常に役立ちます。

例えば、特定のボタンの種類を指定する場合などに使用することができます。

type ButtonType = 'submit' | 'reset' | 'button';

function createButton(type: ButtonType): void {
  // ボタンの生成ロジック
}

createButton('submit');  // OK
// createButton('close');  // エラー: Argument of type '"close"' is not assignable to parameter of type 'ButtonType'.

このコードでは、ボタンの種類をリテラル型として定義しています。

そして、createButton関数ではそのリテラル型を引数として取り、誤ったボタンの種類が渡されることを防ぐことができます。

ただ、リテラル型は非常に便利な機能ではありますが、使いすぎるとコードが冗長になる恐れがあります。

必要な場面でのみ使用することを心がけ、型の再利用や型エイリアスを上手く活用して、コードの管理を行いましょう。

○サンプルコード17:readonly型で変更不可な型を作る

TypeScriptには、オブジェクトや配列の要素を変更不可にするためのreadonly修飾子が用意されています。

この記事では、readonly型を使用して、変更不可の型を作成する方法を詳しく解説します。

このコードでは、readonlyを使って、オブジェクトや配列の要素を変更不可にする方法を表しています。

この例では、最初にオブジェクトのプロパティを変更不可にした後、配列の要素も変更不可にしています。

// オブジェクトのプロパティをreadonlyで定義
type ReadOnlyPerson = {
    readonly name: string;
    readonly age: number;
};

const person: ReadOnlyPerson = {
    name: "山田太郎",
    age: 25
};

// 次のコードはコンパイルエラーとなる
// person.name = "鈴木一郎"; // エラー: Cannot assign to 'name' because it is a read-only property.

// 配列もreadonlyにできる
const numbers: readonly number[] = [1, 2, 3, 4, 5];
// 以下のコードはコンパイルエラーとなる
// numbers.push(6); // エラー: Property 'push' does not exist on type 'readonly number[]'.

上記のコードでは、まずReadOnlyPersonという型を定義しています。

この型の中のプロパティは全てreadonly修飾子で宣言されており、その結果として、この型のオブジェクトのプロパティは再代入できません。

また、配列もreadonlyを使って変更不可にできます。配列をreadonlyにすると、その配列のメソッドの中で内容を変更するようなメソッド(例:pushpop)は使用できなくなります。

このような機能は、関数やメソッドにオブジェクトや配列を引数として渡す際に、その関数内でオブジェクトや配列の内容を変更されたくない場合に非常に役立ちます。

特に大規模なアプリケーションやライブラリの開発時には、データの安全性を保つために活用することが推奨されます。

注意点としては、readonlyはあくまでTypeScriptの型レベルでの制約であり、実際のJavaScriptの実行時にはこの制約は存在しません。

そのため、TypeScriptを使用している場合でも、JavaScriptのコードからはreadonlyの制約を無視して変更することが可能です。

応用例として、関数やクラスのメソッドの引数としてオブジェクトや配列を受け取る際に、その引数をreadonlyとして受け取ることで、関数内での変更を防ぐことができます。

function printPersonInfo(person: ReadOnlyPerson) {
    console.log(`名前: ${person.name}, 年齢: ${person.age}`);
    // person.name = "新しい名前"; // この行はエラーとなる
}

printPersonInfo({ name: "田中花子", age: 30 });

このコードを実行すると、”名前: 田中花子, 年齢: 30″というメッセージがコンソールに表示されます。

関数内でperson.nameへの再代入を試みると、コンパイルエラーが発生します。

このように、readonlyを使用することで、関数やメソッド内でのデータの変更を防ぐことができるため、バグの発生を防ぐことができます。

○サンプルコード18:typeofで型の取得

TypeScriptでは変数の型を動的に取得する際に、typeofを使用することができます。

JavaScriptでも同様の演算子がありますが、TypeScriptでは型情報を持つため、より強力な型取得が可能となっています。

このコードではtypeofを使用して、変数の型を取得するコードを表しています。

この例では、文字列と数値の変数に対して、その型を取得しています。

// 文字列の変数を宣言
let sampleStr: string = "Hello, TypeScript!";

// 数値の変数を宣言
let sampleNum: number = 2023;

// typeofを使用して変数の型を取得
let typeOfStr: string = typeof sampleStr;
let typeOfNum: string = typeof sampleNum;

console.log(typeOfStr);  // "string"
console.log(typeOfNum);  // "number"

上記のコードを実行すると、typeOfStrには”string”という文字列が、typeOfNumには”number”という文字列が格納されます。

それぞれの変数のconsole.logの結果として、文字列の”string”と”number”が出力されます。

TypeScriptではこのようにtypeofを利用して、実行時の変数の型情報を取得することができます。

ただし、この方法は実行時の型情報を取得するものであり、コンパイル時の型情報とは異なる場合がありますので注意が必要です。

また、オブジェクトや配列、関数など、より複雑なデータ構造に対してtypeofを使用した場合の動作にも注意が必要です。

例えば、オブジェクトや配列に対してtypeofを使用すると、”object”という結果が返されます。

具体的な例を紹介します。

let sampleArray: number[] = [1, 2, 3];
let typeOfArray: string = typeof sampleArray;

console.log(typeOfArray);  // "object"

このように、配列に対してtypeofを使用すると”object”という結果が得られます。

このため、より詳細な型情報が必要な場合は、TypeScriptの他の型関連の機能を利用する必要があります。

○サンプルコード19:keyofでオブジェクトのキーの型を取得

TypeScriptは、オブジェクトのキーを型として取得するための非常に便利な組み込み型、keyofを提供しています。

この型は、オブジェクトのプロパティの名前を文字列のリテラル型のユニオンとして取得することができます。

これにより、オブジェクトのキーの名前をコンパイル時に制限することが可能となります。

このコードでは、keyofを使ってオブジェクトのキーの型を取得するコードを表しています。

この例では、オブジェクトの型を定義し、そのオブジェクトのキーを文字列のリテラル型のユニオンとして取得しています。

// オブジェクトの型定義
type Profile = {
    name: string;
    age: number;
    address: string;
};

// keyofを使用してオブジェクトのキーの型を取得
type ProfileKeys = keyof Profile;

上記のコードでは、Profileという型のオブジェクトが定義されています。

その後、keyofを使用してProfileのキーの型をProfileKeysとして取得しています。

この結果、ProfileKeys"name" | "age" | "address"という型になります。

この取得したキーの型は、関数の引数などで特定のオブジェクトのキーのみを受け付けたい場合などに非常に便利です。

例えば、次のような関数でオブジェクトの特定のキーの値を取得することができます。

function getValue(key: ProfileKeys, obj: Profile): any {
    return obj[key];
}

const person: Profile = {
    name: "Taro",
    age: 25,
    address: "Tokyo"
};

// 使用例
const personName = getValue("name", person);  // "Taro"という値が取得される

この関数getValueは、ProfileKeys型のkeyProfile型のobjを受け取り、objからkeyに対応する値を返します。

getValue関数を使用する際、keyとしてProfileに存在しないキーを渡すと、コンパイルエラーが発生します。

このように、keyofはオブジェクトのキーの型を簡単に取得し、その型をさまざまな場面で使用することで、より型安全なコードを書くのに役立ちます。

注意点としては、keyofはオブジェクトのキーの名前を取得するため、オブジェクトの値の型ではなく、キーの型を取得することに注意が必要です。

また、ネストされたオブジェクトの場合、トップレベルのキーのみが取得されます。

○サンプルコード20:indexed access型で特定の型の取得

TypeScriptの組み込み型として、indexed access型は非常に便利で、多様な用途で利用できます。

indexed access型は、オブジェクト型やユニオン型の中から特定のキーに関連付けられた型を取得することができます。

このコードでは、indexed access型を使って、オブジェクトの特定のプロパティの型を取得するコードを表しています。

この例では、Personオブジェクトの”age”プロパティの型を取得しています。

type Person = {
    name: string;
    age: number;
    address: {
        city: string;
        zip: number;
    };
}

type AgeType = Person['age'];  // number型が取得される
type CityType = Person['address']['city'];  // string型が取得される

上記のサンプルコードでは、Person型のageプロパティはnumber型なので、AgeTypenumber型になります。

同様に、addressプロパティの中のcitystring型なので、CityTypestring型として取得されます。

このように、indexed access型はオブジェクトの深い階層にあるプロパティの型を簡単に参照することができるため、大規模なプロジェクトや複雑な型構造を持つプロジェクトで非常に役立ちます。

indexed access型を使用する際の注意点としては、存在しないキーの型を取得しようとするとコンパイルエラーが発生する点です。

したがって、存在するキーを正しく指定することが重要です。

type WrongType = Person['salary'];  // コンパイルエラー!

上記のコードでは、Person型にはsalaryというキーが存在しないため、コンパイルエラーが発生します。

また、indexed access型は、複雑なオブジェクトやネストされたオブジェクトの中の特定の型を取得するのに役立ちます。

type NestedPerson = {
    details: {
        personal: {
            name: string;
            age: number;
        },
        work: {
            title: string;
            department: string;
        }
    }
}

type DepartmentType = NestedPerson['details']['work']['department'];  // string型が取得される

この例では、NestedPerson型の中のworkオブジェクトのdepartmentプロパティの型を取得しています。

その結果、DepartmentTypestring型として定義されます。

結果として、indexed access型を使用することで、特定のキーに関連付けられた型を簡単に取得できるため、TypeScriptでの型の管理がより簡単になります。

○サンプルコード21:conditional型で条件に応じた型を作成

TypeScriptには多彩な組み込み型が存在し、その一つとして「conditional型」というものがあります。

conditional型は、その名の通り、ある条件に応じて型を分岐させるための型です。

このconditional型を使うことで、非常に柔軟に型の振る舞いを定義することが可能になります。

このコードではconditional型を使って、条件に応じて型を切り替えるサンプルコードを表しています。

この例では、文字列型の”hello”か、数字の123を受け取る型をconditional型で表現しています。

type IsString<T> = T extends string ? string : never;
type IsNumber<T> = T extends number ? number : never;
type StringOrNumber<T> = IsString<T> | IsNumber<T>;

// 使用例
let sample1: StringOrNumber<'hello'>; // string
let sample2: StringOrNumber<123>; // number
let sample3: StringOrNumber<boolean>; // エラー!booleanは受け取れない

上記のコードでは、まずIsStringIsNumberという2つのconditional型を定義しています。

それぞれ文字列型や数値型が与えられた場合に、その型を返すようになっています。

そして、最後のStringOrNumber型は、これら2つのconditional型を組み合わせることで、文字列または数値のどちらかの型を持つことができるようになっています。

サンプルコードの使用例部分では、sample1sample2には正しく型が割り当てられていますが、sample3はboolean型が割り当てられており、これはStringOrNumber型の条件に合致しないため、TypeScriptの型チェックでエラーとなります。

このように、conditional型は、特定の条件に基づいて型を制御する際に非常に役立ちます。

特に複雑な型の組み合わせや、ある型が持っているべき特性を条件として定義する際には、このconditional型が大変有効です。

また、conditional型を更に応用することで、より高度な型の操作や制御を行うことも可能です。

例えば、2つのオブジェクト型が与えられた時、その2つの型が全く同じであるかをチェックするような型もconditional型を使用して表現することができます。

次に、このコードが実際にどのような動作をするのか確認してみましょう。

コードを実行した場合、sample1sample2は型のエラーは発生しませんが、sample3にはTypeScriptの型システムからエラーが出力されることが確認できます。

これは、sample3がboolean型を持つため、StringOrNumber型の条件に適合しないからです。

●組み込み型の応用テクニック

TypeScriptの組み込み型は、基本的な使用法だけでなく、さまざまな応用的な技術をもたらします。

ここでは、それらの応用技術に焦点を当て、実際のコードを交えながら解説していきます。

○型ガードとは

型ガードとは、ある変数が特定の型であることをランタイム時に確認し、その後のコード内でその型として扱うことを保証する技術です。

このコードでは、isStringという関数を使って、引数がstring型であるかどうかを判定するコードを表しています。

この例では、型ガードを活用して、引数がstring型である場合とそれ以外の場合で異なる処理を行っています。

function isString(value: any): value is string {
  return typeof value === "string";
}

function printLength(value: any) {
  if (isString(value)) {
    // ここではvalueがstring型であることが保証されている
    console.log(value.length);
  } else {
    console.log("Not a string");
  }
}

printLength("Hello");  // 5を出力
printLength(123);  // Not a stringを出力

このコードを実行すると、printLength("Hello")5を、printLength(123)Not a stringを出力します。

○組み込み型の組み合わせ技

TypeScriptの組み込み型を組み合わせることで、更に高度な型を作ることができます。

このコードでは、ユニオン型と交差型を組み合わせて、新しい型を作成する例を表しています。

この例では、PersonAddressという二つの型を持ち、それらを組み合わせたContact型を定義しています。

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

type Address = {
  street: string;
  city: string;
};

type Contact = Person & Address;

const contact: Contact = {
  name: "Taro",
  age: 25,
  street: "1-2-3 Tokyo",
  city: "Tokyo"
};

console.log(contact.name);  // Taroを出力

上記のコードでは、Contact型にはPersonAddressの両方のプロパティが必要とされています。

そして、console.log(contact.name)を実行すると、Taroという結果が得られます。

●注意点と対処法

TypeScriptの組み込み型は非常に強力で、多岐にわたるタスクを助けてくれます。しかし、その強力さゆえに注意しなければならない点や、誤解を招く部分も存在します。このセクションでは、それらの注意点と、それに対応するための対処法を具体的なサンプルコードとともに解説していきます。

○組み込み型の適切な使用時と落とし穴

TypeScriptの組み込み型を使用する際の一般的な注意点とその対処法について学びましょう。

❶any型の過度な使用

any型はTypeScriptの全ての型を受け入れるため、過度に使用すると型安全性が損なわれる可能性があります。

このコードではany型を使って任意の型のデータを受け入れるコードを表しています。

この例では数字を文字列として扱っています。

let data: any;
data = 123;
console.log(data.charAt(0)); // 実行時エラー

このように、any型を使用することでコンパイル時にはエラーが出ないが、実行時にエラーが発生するリスクが高まります。

❷unknown型の誤用

unknown型もany型と同じように任意の型を受け入れますが、安全に使用するための制約があります。

このコードではunknown型を使ってデータを受け入れ、その後で型チェックを行うコードを表しています。

この例では文字列のデータを受け取り、文字列のメソッドを利用しています。

let value: unknown;
value = "TypeScript";
if (typeof value === "string") {
  console.log(value.toUpperCase()); // "TYPESCRIPT"
}

このように、unknown型を使用する際は、その型が確定してからデータを操作することが必要です。

○型安全を保つためのヒント

❶型アサーションの活用

型が明確にわかる場面で、型アサーションを使用して型を明示的に指定することができます。

このコードではunknown型のデータを受け取り、型アサーションを用いて明示的に文字列型として扱うコードを表しています。

この例では文字列のデータを受け取り、大文字に変換しています。

let unknownData: unknown = "Hello, TypeScript!";
let stringData = unknownData as string;
console.log(stringData.toUpperCase()); // "HELLO, TYPESCRIPT!"

このように型アサーションを活用することで、型が確定している場合に安全にデータの操作が可能となります。

❷適切な組み込み型の選択

より詳細な型情報を持つ組み込み型を選択することで、型エラーのリスクを低減できます。

例えば、数値か文字列を受け入れる変数がある場合、anyunknownよりもユニオン型を利用することで、より具体的な型情報を提供することができます。

このコードでは数字または文字列のデータを受け入れるユニオン型を表しています。

この例では数字と文字列をそれぞれ受け取り、適切に処理しています。

let numberOrString: number | string;
numberOrString = 123;
console.log(numberOrString); // 123
numberOrString = "Hello";
console.log(numberOrString); // "Hello"

●カスタマイズ方法

TypeScriptの組み込み型は非常に便利ですが、場合によってはカスタマイズしたいと思うこともあるでしょう。

ここでは、カスタマイズの方法をいくつか紹介します。

○独自の型エイリアスの作り方

TypeScriptでは、型エイリアスを用いて独自の型を定義することができます。

これにより、特定のプロジェクトやビジネスロジックに特化した型を作成できます。

// UserTypeという新しい型を定義
type UserType = {
    id: number;
    name: string;
    email: string;
};

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

この例では、id, name, emailの3つのプロパティを持ったオブジェクトの型を表しています。

このようにして、特定のデータ構造に合わせた型を独自に作ることができます。

○既存の組み込み型の拡張方法

TypeScriptの組み込み型は固定されていますが、型エイリアスや交差型を使用して、これを拡張することも可能です。

例えば、既存のPerson型に新しいプロパティを追加したい場合、次のように交差型を使って拡張することができます。

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

type Employee = Person & {
    employeeId: number;
};

const worker: Employee = {
    name: "山田",
    age: 30,
    employeeId: 12345
};

このコードでは、既存のPerson型を継承してEmployee型を作成しています。

この例では、Employee型はPerson型のすべてのプロパティと、新たにemployeeIdを持っています。

このようにして、TypeScriptの組み込み型や既存の型を基にして、新しい型を作成することができます。

まとめ

TypeScriptの組み込み型は、現代のフロントエンドやバックエンドの開発において非常に重要な役割を果たしています。

本ガイドでは、TypeScriptの組み込み型の基本から応用、注意点、カスタマイズ方法までを徹底的に解説してきました。

このガイドを読み終えたあなたは、TypeScriptの組み込み型に関する知識を十分に習得し、今後のプロジェクトにおいて、その知識を活かすことができるでしょう。

常に最新の情報をチェックし、さらなるスキルアップを目指してください。