1. ホーム
  2. 記事一覧
  3. 初心者向け!TypeScript型入門

2024.12.17

初心者向け!TypeScript型入門

TypeScriptの型定義に挑戦してみよう

TypeScriptの「型定義」について、「型って本当に必要?」「そもそもどう使うのかわからない…」と思ったことはありませんか?特に動的型付け言語に慣れている方にとって、静的型付けは少しハードルが高く感じられるかもしれません。

型定義とは、データの種類や形を明確に指定することで、コードの安全性や可読性を向上させる仕組みです。コードを書く段階で潜在的な問題を発見できるため、バグを未然に防ぎ、チーム開発でも役立つ信頼性の高いコードを実現できます。

この記事では、TypeScript初心者が理解しやすいように「型定義」の基本をやさしく解説します。動的型付け言語で学んできたフロントエンド初心者の方でも、安心して読み進められる内容です。型定義に対する苦手意識をなくして、一緒にTypeScriptの楽しさを体験してみましょう。

この記事について

目的

  • TypeScriptの型定義の仕組みを理解し、使えるようになること

対象者

  • フロントエンドエンジニアを目指す初心者の方
  • 静的言語の型について知りたい方

TypeScriptの基本についてさらに詳しく学びたい場合は、以下の記事を参考にしてください。初心者向けのハンズオン形式で解説しています。

フロントエンジニアを目指す初心者向け TypeScriptの基礎を解説

https://envader.plus/article/340

TypeScriptの型付けの仕組み

はじめに型付けの仕組みについて、学んでおきましょう。TypeScriptは、JavaScriptに「型」を追加することで、コードの安全性と保守性を向上させることを目的としています。特に、静的型付けの特性を利用することで、開発中に発生しがちなエラーを未然に防ぐことができます。

このセクションでは、TypeScriptの型付けの仕組みを理解するために必要な以下の基本4つを紹介します。

  • 動的型付けと静的型付けの違い
  • 基本的な型付け方法
  • 型注釈と型推論による型付け
  • プリミティブ値の型

動的型付けと静的型付けの違い

JavaScriptは「動的型付け」の言語であり、変数にどんな型の値でも自由に代入できますが、コードを実行しないとバグがあるかを確かめられません。

一方、TypeScriptは「静的型付け」の特徴を持ち、コードを書く段階で変数や関数の型を指定します。これによりコード実行前のコンパイル時に、エラーを発見することが可能です。コードと共にJavaScriptとTypeScriptの違いついてさらに確認しましょう。

JavaScriptの例(動的型付け)

let value = 10; // 初めは数値
value = "hello"; // 後から文字列を代入

このコードは動作しますが、valueの型が変わることでバグが発生する可能性があります。

TypeScriptの例(静的型付け)

let value: number = 10; // 型を明示的に指定
value = "hello"; // エラー: 'string'型を'number'型に割り当てることはできません

このように型を明示することで、予期しないデータ型の変化やバグを早期に発見し、コードの信頼性が高まります。

基本的な型付け方法

TypeScriptで型付けをするには、変数名の後にコロン(:)と型タイプを記述します。

// 基本型定義の記述方法

const 変数名: 型タイプ = 変数に代入する値;

// 記述例

const greeting: string = "Hello, エンベーダー!"

型注釈と型推論による型付け

TypeScriptでは、変数や関数に型を付ける方法として、型注釈と型推論があります。これらは、コードの安全性を高めるための基本的な仕組みです。

型注釈

基本的な型付け方法で説明したような、開発者が明示的に変数や関数の型を指定する方法です。これにより、変数や関数がどのようなデータを扱うのかを明確に示すことができます。特にチーム開発や複雑なコードベースで役立ち、他の開発者がコードを読みやすくなるだけでなく、間違ったデータ操作を防ぐ効果があります。

let age: number = 25; // ageは数値型

型推論

型推論は、開発者が型を明示的に記述しなくても、TypeScriptがコードの内容から適切な型を自動的に推測する仕組みです。変数や関数の値や構造を分析して、適切な型を推測します。たとえば、次のような例を見てみましょう。

let message = "Hello, エンベーダー!";

ここで、message変数には"Hello, エンベーダー!"という文字列が代入されています。この場合、TypeScriptはmessagestring型であると自動的に判断します。開発者が明示的に型を書かなくても、TypeScriptはこの情報を利用してコードの安全性を確保します。

ただし、型推論は万能ではないため、意図が明確でない場合や曖昧なデータ型が含まれる場合には型注釈を利用することが推奨されます。

プリミティブ値の型について

TypeScriptでは、以下の基本的なプリミティブ値の型が使用できます。

型タイプ内容
string文字列型
number数値型
boolean真偽値型
bigint大きな整数を扱う型
symbol一意の識別子を表す型
undefined値が未定義の状態を表す型
null値が存在しない状態を表す型

これらの型はTypeScriptの型システムの基礎を構成し、コードの安全性を確保するために使用されます。

TypeScriptの型の定義方法について

TypeScriptの基本的な型付けの仕組みを学びました。このセクションでは、以下の型の定義方法について解説します。開発に合わせた型定義方法を採用することで、コードの可読性と効率を向上させることが可能です。

  • 配列型
  • オブジェクト型
  • 関数の型と引数と戻り値の型
  • リテラル型
  • ユニオン型
  • 型エイリアス
  • インターフェース

配列の型定義

TypeScriptでは、配列に対して「要素がどの型になるか」を明確に定義できます。ただし、基本的には配列内の要素すべてが同じ型でなければなりません。

配列型の基本定義

配列型は、型名の後に[]を付けます。

let numbers: number[];
numbers = [1, 2, 3]; // 数値型のみの配列

let strings: string[];
strings = ["a", "b", "c"]; // 文字列型のみの配列

または、Array<型タイプ>を用いた書き方もあります。

let numbers: Array<number>;  // Array<型タイプ>で型定義
numbers = [1, 2, 3];

配列の型定義方法を2つ紹介しましたが、書き方が異なるだけでコードの意味することは同じす。開発のプロジェクトやチームで書き方を統一しておくとよいでしょう。

複数の型を扱いたい場合

配列内に複数の型を許容したい場合は、ユニオン型を用いると定義することができます。

let mixed: (string | number)[]; //ユニオン型で配列に複数の方を定義
mixed = [1, "a", 2, "b"]; // 数値と文字列を混在させた配列

この記述方法については、後述するユニオン型のセクションで詳しく解説します。

オブジェクト型での定義

オブジェクト型では、オブジェクトの各プロパティに対して型を指定できます。これにより、オブジェクトの構造を明確に定義し、意図しないデータ操作を防ぐことができます。

オブジェクト型の基本定義

オブジェクト型を定義する際は、オブジェクトリテラル{}を使用し、{ プロパティ名: 型; ... }の形式で各プロパティの型を指定します。以下は基本的な例です。

let user: { name: string; age: number };

user = {
  name: "Alice",
  age: 25,
};

オブジェクト型ではプロパティごとに型を指定することができます。今回の例では、userオブジェクトのnameプロパティにはstring型、ageプロパティはnumber型であることを指定しています。これにより、型定義に合ったオブジェクトのみが許容され、型定義に含まれていないプロパティを追加したり、型が一致しない値を渡すとエラーになります。

定義した型の条件を満たす必要がある

オブジェクト型では、型で定義したすべてのプロパティが存在している必要があります。各プロパティには対応する値を必ず持つ必要があり、それが欠けている場合はエラーとなります。

例: 必須プロパティが不足している場合

let user: { name: string; age: number };

user = {
  name: "Alice",
  // age プロパティが不足しているためエラーになる
};

userオブジェクトにはnameageの両方が必須ですが、この場合ageプロパティが欠けています。もし、あるプロパティが必須ではなく、存在しないことを許容したい場合は、次に紹介するオプションプロパティを使用するとよいでしょう。

オプションプロパティ

オプションプロパティは、値を持たない状態(プロパティが存在しない状態)を許容するための仕組みです。特定のプロパティに?を付けることで、必須ではなく省略可能であることを示します。

例: オプションプロパティを使用する場合

let user: { name: string; age?: number };  // ageにオプションプロパティを指定

user = {
  name: "Alice",
  // age プロパティを省略できる
};

age?を付けることで、プロパティが省略可能になります。オプションプロパティは、後から値を追加する場合や、データが不完全な状態を許容する際に便利です。

TypeScriptでは、オブジェクト型を使ってオブジェクトの構造を明確に定義できます。プロパティが不足している場合や型が異なる場合はエラーとなるため、意図しないデータ操作を防ぐことができます。

関数の型と引数と戻り値の型定義

TypeScriptでは、関数の型を定義する際に、引数の型と戻り値の型を明示的に指定することができます。この型定義により、関数の使い方が明確になり、意図しないデータ操作を防ぐことができます。

関数の型の基本定義

関数の方は定義は、関数名の後に(引数の型): 戻り値の型と指定します。

function add(a: number, b: number): number {
  return a + b;
}

この例では引数abnumber型を指定しています。この型定義により、add関数には数値型の引数しか渡せません。次に(): numberの部分で戻り値にnumber型を指定し、関数の戻り値は数値型を返すことを示しています。

アロー関数での型定義

アロー関数でも、引数や戻り値に型を付けることができます。書き方が少し異なるだけで、通常の関数と同じように型を指定できます。

const add = (a: number, b: number): number => a + b;

通常の関数の記述例と同様に、引数とその戻り値に数値型を指定しています。

関数に型定義をすることで、関数の動作がより理解しやすくなります。例えばadd(1, "two")のような誤った引数の組み合わせを渡そうとした場合、TypeScriptがエラーを教えてくれるため、間違ったデータの扱いを防げます。

リテラル型の型定義

リテラル型は、特定の値そのものを型として扱うものです。通常の型(例:string, number)は、その型に含まれるすべての値を許容します。一方でリテラル型は、プリミティブ型の特定の値だけを代入可能にします。これにより、データを意図した範囲内に限定し、コードの安全性や可読性を向上させ、予期しないエラーを未然に防ぐことができます。

リテラル型の基本定義

以下はリテラル型の基本的な定義方法と例です。

// リテラル型定義の記述方法
let 変数名: 特定の値 = 特定の値;

// 記述例
let greeting: "hello" = "hello"; // greetingは"hello"以外を代入できない

constを使用した場合の注意点

constを使う場合、リテラル型は自動的に適用されます。これは、const宣言によって変数が再代入不可になるためです。

// constを使用する例
const greeting = "hello"; // 型: "hello"となり、greetingは"hello"以外を代入できない

リテラル型は、変数が固定された特定の値のみを取得したい場合に有用です。さらに、リテラル型はユニオン型と併用することで、複数の選択肢を型として定義でき、その効果を活かすことができます。ユニオン型との併用については、次のセクションで解説します。

ユニオン型の型定義

ユニオン型は、1つの変数に複数の異なる型を許容するための仕組みです。これにより、柔軟な型定義が可能になり、状況に応じたデータの扱いがしやすくなります。

ユニオン型の基本定義

ユニオン型は、|(パイプ)を使って複数の型を結合します。次の例では、valueという変数に string型とnumber型を許容しています。

let value: string | number;
value = "hello"; // OK: string型
value = 123;     // OK: number型
value = true;    // エラー: boolean型は許容されていない

ユニオン型と配列の組み合わせ

ユニオン型は配列型とも組み合わせることができます。配列内の要素に複数の型を許容したい場合に便利です。

let mixed: (string | number)[]; // 変数に2つの型を定義
mixed = [1, "a", 2, "b"]; // 数値と文字列を混在させた配列を定義できる

ユニオン型とリテラル型の組み合わせ

リテラル型をユニオン型を組み合わせて使用すると、複数の特定の値を型として扱うことができます

type Status = "success" | "error" | "loading";

let currentStatus: Status;

currentStatus = "success"; // OK
currentStatus = "error";   // OK
currentStatus = "pending"; // エラー: 型 'pending' は型 'Status' に適合しない

Status型は "success", "error", "loading" のいずれかしか許容しません。型以外の値(例: "pending")を代入しようとするとエラーになります。

constとリテラル型の特性を活かす

constを使いリテラル型が自動的に適用される特性を活かし、ユニオン型と組み合わせると、さらに簡潔で安全なコードが書けます。

const state: "success" | "error" | "loading" = "success";

具体的な値を指定しつつ、ユニオン型の特性で選択肢を制限できます。型安全を確保しつつ、コードが分かりやすくなるメリットがあります。

型エイリアスの型定義

型エイリアスは、型に名前を付けて再利用可能にする仕組みです。複雑な型を簡潔にまとめ、コードの可読性や保守性を向上させます。

型エイリアスの宣言方法

型エイリアスを宣言するにはtypeキーワードを使用します。以下のように、任意の型に名前を付けることができます。

type StringAndNumber = string | number;

この例では、StringAndNumberという名前が付けられた型が、string型とnumber型のユニオン型であることを示しています。

型エイリアスの使用方法

型エイリアスは、変数や関数の型定義に簡単に再利用できます。

変数への適用

以下の例では、ユニオン型を型エイリアスStringAndNumberとして定義し、変数に適用しています。

type StringAndNumber = string | number;

// 変数nameとageに、型エイリアスStringAndNumberを定義
const name: StringAndNumber = "Alice"; // OK
const age: StringAndNumber = 25; // OK

オブジェクト型への適用

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

// userオブジェクトに、型エイリアスUserを定義
const user: User = {
  name: "Alice",
  age: 25,
};

型エイリアスは、複雑な型定義を簡潔にまとめるための便利なツールです。同じ型定義を複数の場所で使用する場合、一箇所に定義をまとめることで、修正やメンテナンスが容易になります。また、型に名前を付けることで、コードの意図を明確に伝えることができます。

インターフェースの型定義

インターフェースは、オブジェクト型の構造を定義するための仕組みです。主に、オブジェクトが持つプロパティや型を事前に指定するために使用します。

インターフェースの基本定義

以下は、Userというインターフェースを定義し、それを使用してオブジェクトの型を指定する基本的な例です。

// Userインターフェースを使用して、オブジェクトの構造を定義
interface User {
  name: string;
  age: number;
}

const user: User = { name: "Alice", age: 25 }; // User型に適合するオブジェクト

このように、オブジェクトの構造を一目で把握できるため、可読性が向上します。

型の拡張(継承)が可能

インターフェースは、型の拡張(継承) をサポートしています。これにより、既存の型に新しいプロパティを追加するなど、柔軟な型定義が可能です。

interface User {
  name: string;
  age: number;
}

// Userインターフェースを継承して新しいプロパティを追加
interface Admin extends User {
  role: string; // roleプロパティを追加
}

const admin: Admin = { name: "Bob", age: 30, role: "superadmin" }; // Admin型に適合

AdminUserを継承し、さらにroleプロパティを追加しています。これにより、既存の型を基に新しい型を簡単に作成できます。

インターフェースは、オブジェクト型の構造を明確に定義し、一貫性を保ちながら型の拡張や柔軟な管理を可能にする仕組みです。特にチーム開発や大規模なプロジェクトで、統一された型を用いることで保守性が向上します。

この記事で学んだこと

ここまでTypeScriptの型について学んできました。最後に大切なポイントを振り返りましょう。

TypeScriptの型定義とは、データの種類や形を明確に指定することで、コードの安全性や可読性を向上させる仕組みです。静的型付けの特性を利用することで、開発中に発生しがちなエラーを未然に防ぐことができます。

TypeScriptの型の定義方法についてこの記事では、以下の定義方法を学びました。

  • 配列型
  • オブジェクト型
  • 関数の型と引数と戻り値の型
  • リテラル型
  • ユニオン型
  • 型エイリアス
  • インターフェース

TypeScriptの型は、開発者にとって非常に強力なツールです。この記事で学んだ基礎をもとに、ぜひご自身でコードを書いて試してみてください。実際に型定義を活用することで、エラーを防ぎながら、より安全で可読性の高いコードを実感できるはずです。

型定義を少しずつプロジェクトに取り入れ、さらに複雑な型や応用的な使い方にも挑戦してみましょう。この学びをきっかけに、TypeScriptの世界を深く探求し、次のステップへ進んでみてください。

参考資料

以下のリンクは、この記事で説明した手順や概念に関連する参考資料です。より詳しく学びたい方は、ぜひご覧ください。

【番外編】USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

IT未経験者必見 USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

プログラミング塾に半年通えば、一人前になれると思っているあなた。それ、勘違いですよ。「なぜ間違いなの?」「正しい勉強法とは何なの?」ITを学び始める全ての人に知って欲しい。そう思って書きました。是非読んでみてください。

「フリーランスエンジニア」

近年やっと世間に浸透した言葉だ。ひと昔まえ、終身雇用は当たり前で、大企業に就職することは一種のステータスだった。しかし、そんな時代も終わり「優秀な人材は転職する」ことが当たり前の時代となる。フリーランスエンジニアに高価値が付く現在、ネットを見ると「未経験でも年収400万以上」などと書いてある。これに釣られて、多くの人がフリーランスになろうとITの世界に入ってきている。私もその中の1人だ。数年前、USBも知らない状態からITの世界に没入し、そこから約2年間、毎日勉学を行なった。他人の何十倍も努力した。そして、企業研修やIT塾で数多くの受講生の指導経験も得た。そこで私は、伸びるエンジニアとそうでないエンジニアをたくさん見てきた。そして、稼げるエンジニア、稼げないエンジニアを見てきた。

「成功する人とそうでない人の違いは何か?」

私が出した答えは、「量産型エンジニアか否か」である。今のエンジニア市場には、量産型エンジニアが溢れている!!ここでの量産型エンジニアの定義は以下の通りである。

比較的簡単に学習可能なWebフレームワーク(WordPress, Rails)やPython等の知識はあるが、ITの基本概念を理解していないため、単調な作業しかこなすことができないエンジニアのこと。

多くの人がフリーランスエンジニアを目指す時代に中途半端な知識や技術力でこの世界に飛び込むと返って過酷な労働条件で働くことになる。そこで、エンジニアを目指すあなたがどう学習していくべきかを私の経験を交えて書こうと思った。続きはこちらから、、、、

note記事3000いいね超えの殿堂記事 今すぐ読む

エンベーダー編集部

エンベーダーは、ITスクールRareTECHのインフラ学習教材として誕生しました。 「遊びながらインフラエンジニアへ」をコンセプトに、インフラへの学習ハードルを下げるツールとして運営されています。

RareTECH 無料体験授業開催中! オンラインにて実施中! Top10%のエンジニアになる秘訣を伝授します! RareTECH講師への質疑応答可

関連記事