はじめに#
TypeScriptを学び始めると、最初に出会うのが「型」という概念です。JavaScriptでは変数にどんな値でも自由に入れられますが、TypeScriptでは各変数に「この変数には文字列だけが入る」「この変数には数値だけが入る」といった制約を付けられます。
本記事では、TypeScriptの基本となるプリミティブ型(string、number、boolean、null、undefined、symbol、bigint)と、型注釈の書き方、型推論の仕組み、そしてany型とunknown型の違いについて詳しく解説します。
この記事を読み終えると、以下のことができるようになります。
- TypeScriptのプリミティブ型を正しく使い分けられる
- 変数・関数の引数・戻り値に適切な型注釈を付けられる
- 型推論の仕組みを理解し、不要な型注釈を省略できる
- any型とunknown型の違いを理解し、安全なコードを書ける
実行環境・前提条件#
前提知識#
- JavaScriptの基本構文(変数、関数、オブジェクト)の理解
- TypeScriptの概要を把握していること(TypeScriptとは何かを参照)
動作確認環境#
| ツール |
バージョン |
| Node.js |
20.x以上 |
| TypeScript |
5.7以上 |
| VS Code |
最新版 |
本記事のサンプルコードは、TypeScript Playgroundで動作確認できます。ローカル環境で実行する場合は、開発環境構築ガイドを参照してください。
TypeScriptのプリミティブ型#
TypeScriptには、JavaScriptと同様に7つのプリミティブ型が存在します。プリミティブ型とは、オブジェクトではない最も基本的なデータ型のことです。
graph TB
A[TypeScriptの型] --> B[プリミティブ型]
A --> C[オブジェクト型]
B --> D[string]
B --> E[number]
B --> F[boolean]
B --> G[null]
B --> H[undefined]
B --> I[symbol]
B --> J[bigint]string型 - 文字列を表す型#
string型は、文字列を表す型です。シングルクォート、ダブルクォート、バッククォート(テンプレートリテラル)で囲まれた値が該当します。
1
2
3
4
5
6
7
8
9
10
11
12
|
// string型の基本
const greeting: string = "Hello, TypeScript!";
const name: string = 'Taro';
const message: string = `Welcome, ${name}!`;
// 文字列メソッドの使用
const upperCase: string = greeting.toUpperCase();
const length: number = greeting.length;
console.log(message); // "Welcome, Taro!"
console.log(upperCase); // "HELLO, TYPESCRIPT!"
console.log(length); // 18
|
TypeScriptでは、String(大文字始まり)ではなく、string(小文字)を使用することが推奨されています。大文字のStringはラッパーオブジェクト型を指し、プリミティブ型とは異なります。
1
2
3
4
5
|
// 推奨される書き方
const good: string = "Hello";
// 非推奨(ラッパーオブジェクト型)
const bad: String = "Hello"; // 動作するが非推奨
|
number型 - 数値を表す型#
number型は、整数と浮動小数点数の両方を表す型です。JavaScriptと同様に、TypeScriptではintやfloatといった区別はありません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// number型の基本
const integer: number = 42;
const float: number = 3.14;
const negative: number = -100;
// 特殊な数値
const infinity: number = Infinity;
const notANumber: number = NaN;
// 2進数、8進数、16進数リテラル
const binary: number = 0b1010; // 10
const octal: number = 0o744; // 484
const hex: number = 0xFF; // 255
// 数値の区切り文字(可読性向上)
const million: number = 1_000_000;
const bytes: number = 0xFF_FF_FF_FF;
console.log(integer); // 42
console.log(million); // 1000000
|
number型はIEEE 754倍精度浮動小数点数を使用するため、大きな整数を扱う場合は後述するbigint型の使用を検討してください。
1
2
3
4
5
6
|
// number型の精度限界の例
const maxSafe: number = Number.MAX_SAFE_INTEGER; // 9007199254740991
const overflow: number = maxSafe + 1;
const overflow2: number = maxSafe + 2;
console.log(overflow === overflow2); // true(精度の問題で同じ値になる)
|
boolean型 - 真偽値を表す型#
boolean型は、trueまたはfalseの2つの値のみを持つ型です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// boolean型の基本
const isActive: boolean = true;
const isDeleted: boolean = false;
// 比較演算の結果はboolean型
const isEqual: boolean = 10 === 10; // true
const isGreater: boolean = 5 > 3; // true
// 論理演算の結果もboolean型
const and: boolean = true && false; // false
const or: boolean = true || false; // true
const not: boolean = !true; // false
// 関数の戻り値としてのboolean
function isEven(num: number): boolean {
return num % 2 === 0;
}
console.log(isEven(4)); // true
console.log(isEven(7)); // false
|
null型とundefined型 - 値が存在しないことを表す型#
TypeScriptでは、nullとundefinedはそれぞれ独立した型として扱われます。
1
2
3
4
5
6
7
8
9
|
// null型
const nullValue: null = null;
// undefined型
const undefinedValue: undefined = undefined;
// 変数を宣言のみした場合はundefined
let notInitialized: undefined;
console.log(notInitialized); // undefined
|
nullとundefinedの使い分けには諸説ありますが、一般的には以下のように使い分けられます。
| 値 |
意味 |
使用シーン |
| null |
意図的に「値がない」ことを表す |
データベースの空の値、意図的な初期化 |
| undefined |
未定義、値が設定されていない |
未初期化の変数、省略されたオプション引数 |
TypeScriptのstrictNullChecksオプション(推奨設定)を有効にすると、nullとundefinedを他の型に代入できなくなり、より安全なコードが書けます。
1
2
3
4
5
6
7
8
|
// strictNullChecksがtrueの場合
let name: string = "Taro";
name = null; // エラー: Type 'null' is not assignable to type 'string'
name = undefined; // エラー: Type 'undefined' is not assignable to type 'string'
// nullを許容する場合はユニオン型を使用
let nullableName: string | null = "Taro";
nullableName = null; // OK
|
symbol型 - 一意の識別子を表す型#
symbol型は、ES2015で導入された一意で不変のプリミティブ値です。主にオブジェクトのプロパティキーとして使用されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// symbol型の基本
const id1: symbol = Symbol("id");
const id2: symbol = Symbol("id");
// 同じ説明でも異なるシンボル
console.log(id1 === id2); // false
// オブジェクトのプロパティキーとして使用
const KEY: symbol = Symbol("key");
const obj = {
[KEY]: "secret value",
name: "visible"
};
console.log(obj[KEY]); // "secret value"
console.log(obj.name); // "visible"
|
symbol型は、オブジェクトのプロパティを外部から隠蔽したい場合や、プロパティ名の衝突を避けたい場合に有効です。
bigint型 - 大きな整数を表す型#
bigint型は、number型では正確に表現できない大きな整数を扱うための型です。ES2020で導入されました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// bigint型の基本(リテラルには n を付ける)
const big1: bigint = 9007199254740991n;
const big2: bigint = BigInt(9007199254740991);
// 演算
const sum: bigint = big1 + 1n;
const product: bigint = 100n * 200n;
console.log(sum); // 9007199254740992n
console.log(product); // 20000n
// numberとbigintは混在できない
const num: number = 10;
const bigNum: bigint = 10n;
// const result = num + bigNum; // エラー: Operator '+' cannot be applied to types 'number' and 'bigint'
|
bigint型を使用する場合は、tsconfig.jsonのtargetオプションをES2020以上に設定する必要があります。
型注釈の書き方#
型注釈(Type Annotation)とは、変数や関数の引数・戻り値に対して明示的に型を指定する構文です。
変数への型注釈#
変数には、変数名の後にコロン(:)と型名を記述して型注釈を付けます。
1
2
3
4
5
6
7
|
// 変数への型注釈
const message: string = "Hello";
let count: number = 0;
let isReady: boolean = false;
// 型注釈と異なる値を代入するとエラー
count = "invalid"; // エラー: Type 'string' is not assignable to type 'number'
|
関数の引数への型注釈#
関数の引数には、各引数名の後に型注釈を付けます。
1
2
3
4
5
6
7
8
9
10
11
|
// 関数の引数に型注釈
function greet(name: string, age: number): void {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
// 正しい呼び出し
greet("Taro", 25);
// 型が一致しない呼び出しはエラー
greet(123, "Taro"); // エラー: 引数の型が一致しない
greet("Taro"); // エラー: 引数が不足
|
関数の戻り値への型注釈#
関数の戻り値には、引数リストの閉じ括弧の後に型注釈を付けます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 戻り値の型注釈
function add(a: number, b: number): number {
return a + b;
}
function getMessage(): string {
return "Hello, TypeScript!";
}
// 戻り値がない関数はvoid型
function logMessage(message: string): void {
console.log(message);
// returnなし、またはreturn;のみ
}
// 実行が完了しない関数はnever型
function throwError(message: string): never {
throw new Error(message);
}
|
アロー関数への型注釈#
アロー関数にも同様に型注釈を付けられます。
1
2
3
4
5
6
7
8
9
10
11
12
|
// アロー関数の型注釈
const multiply = (a: number, b: number): number => {
return a * b;
};
// 省略形
const double = (n: number): number => n * 2;
// 関数型の変数に代入する場合
type MathOperation = (a: number, b: number) => number;
const subtract: MathOperation = (a, b) => a - b;
|
TypeScriptの型推論#
型推論(Type Inference)とは、TypeScriptが明示的な型注釈がなくても、コードの文脈から自動的に型を推測する機能です。
変数宣言時の型推論#
変数を初期化する際、初期値から型が推論されます。
1
2
3
4
5
6
7
8
9
|
// 型推論により型が決定される
const message = "Hello"; // string型と推論
const count = 42; // number型と推論
const isActive = true; // boolean型と推論
const items = [1, 2, 3]; // number[]型と推論
// 推論された型は明示的な型注釈と同等
// 以下は上記と同じ意味
const message2: string = "Hello";
|
VS Codeなどのエディタでは、変数にカーソルを合わせると推論された型を確認できます。
関数の戻り値の型推論#
関数の戻り値も、return文から自動的に推論されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 戻り値がnumber型と推論される
function add(a: number, b: number) {
return a + b;
}
// 戻り値がstring型と推論される
function greet(name: string) {
return `Hello, ${name}!`;
}
// 複数のreturn文がある場合はユニオン型
function getValue(flag: boolean) {
if (flag) {
return "success";
}
return 0;
}
// 戻り値は string | number 型
|
型推論と型注釈の使い分け#
型推論を活用することで、コードを簡潔に保てます。ただし、以下のケースでは明示的な型注釈が推奨されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// ケース1: 初期値がない変数
let laterAssigned: string; // 型注釈が必要
laterAssigned = "Hello";
// ケース2: 関数のパラメータ(常に型注釈が必要)
function process(data: string): void {
console.log(data);
}
// ケース3: 複雑なオブジェクト(可読性向上のため)
interface User {
id: number;
name: string;
email: string;
}
const user: User = {
id: 1,
name: "Taro",
email: "taro@example.com"
};
// ケース4: 公開API(ドキュメントとして)
export function calculateTotal(prices: number[]): number {
return prices.reduce((sum, price) => sum + price, 0);
}
|
any型とunknown型の違い#
TypeScriptには、どんな型の値でも受け入れる特殊な型としてanyとunknownがあります。両者は似ているようで、安全性に大きな違いがあります。
any型 - 型チェックを無効化する型#
any型は、TypeScriptの型チェックを完全に無効化します。
1
2
3
4
5
6
7
8
9
10
11
|
// any型は何でも許容する
let anything: any = "Hello";
anything = 42;
anything = true;
anything = { name: "Taro" };
// any型の値に対する操作はすべて許可される(危険)
anything.foo(); // エラーにならない
anything.bar.baz; // エラーにならない
anything[0]; // エラーにならない
new anything(); // エラーにならない
|
any型を使用すると、実行時エラーのリスクが高まります。JavaScriptからの移行時など、一時的な使用にとどめることが推奨されます。
unknown型 - 安全な「何でも型」#
unknown型は、any型と同様にあらゆる値を代入できますが、その値を使用する際に型チェックが必要です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// unknown型は何でも代入できる
let something: unknown = "Hello";
something = 42;
something = true;
// unknown型の値をそのまま使用するとエラー
// something.toUpperCase(); // エラー: Object is of type 'unknown'
// something + 1; // エラー: Object is of type 'unknown'
// 型ガードで型を絞り込めば使用可能
if (typeof something === "string") {
console.log(something.toUpperCase()); // OK
}
if (typeof something === "number") {
console.log(something + 1); // OK
}
|
any型とunknown型の比較#
| 特徴 |
any |
unknown |
| あらゆる値を代入できる |
はい |
はい |
| 他の型の変数に代入できる |
はい |
いいえ(型ガードが必要) |
| プロパティにアクセスできる |
はい |
いいえ(型ガードが必要) |
| メソッドを呼び出せる |
はい |
いいえ(型ガードが必要) |
| 型安全性 |
低い |
高い |
| 推奨度 |
非推奨 |
条件付きで推奨 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 実践的な例: APIレスポンスの処理
function processApiResponse(response: unknown): string {
// unknownなので、使用前に型チェックが必要
if (typeof response === "object" && response !== null && "message" in response) {
const obj = response as { message: string };
return obj.message;
}
return "Unknown response";
}
// any型を使った場合(非推奨)
function processApiResponseUnsafe(response: any): string {
// 型チェックなしで使用可能だが、実行時エラーのリスクあり
return response.message; // responseがnullの場合にクラッシュ
}
|
noImplicitAnyオプション#
tsconfig.jsonでnoImplicitAnyオプション(strictに含まれる)を有効にすると、暗黙的なany型を禁止できます。
1
2
3
4
5
6
7
8
9
10
11
|
// noImplicitAny: true の場合
// 型注釈がないとエラー
function greet(name) { // エラー: Parameter 'name' implicitly has an 'any' type
return `Hello, ${name}!`;
}
// 型注釈を付ければOK
function greetSafe(name: string): string {
return `Hello, ${name}!`;
}
|
実践的なコード例#
ここまで学んだ内容を活用した実践的なコード例を紹介します。
ユーザー情報を処理する関数#
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// プリミティブ型を活用した関数
function formatUserInfo(
name: string,
age: number,
isAdmin: boolean
): string {
const role: string = isAdmin ? "管理者" : "一般ユーザー";
return `${name}さん(${age}歳)- ${role}`;
}
// 使用例
const info: string = formatUserInfo("田中太郎", 30, false);
console.log(info); // "田中太郎さん(30歳)- 一般ユーザー"
|
安全な数値計算#
1
2
3
4
5
6
7
8
9
10
11
12
|
// unknown型を使った安全な数値計算
function safeAdd(a: unknown, b: unknown): number | null {
if (typeof a === "number" && typeof b === "number") {
return a + b;
}
return null;
}
// 使用例
console.log(safeAdd(10, 20)); // 30
console.log(safeAdd("10", 20)); // null
console.log(safeAdd(undefined, 5)); // null
|
nullableな値の処理#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// nullを許容する型の処理
function getDisplayName(
firstName: string,
lastName: string | null
): string {
if (lastName === null) {
return firstName;
}
return `${lastName} ${firstName}`;
}
// 使用例
console.log(getDisplayName("太郎", "田中")); // "田中 太郎"
console.log(getDisplayName("太郎", null)); // "太郎"
|
型推論を活用した配列処理#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 型推論により、要素の型が自動的に決定される
const numbers = [1, 2, 3, 4, 5]; // number[]と推論
// mapの結果も型推論される
const doubled = numbers.map(n => n * 2); // number[]と推論
// filterの結果も型推論される
const evens = numbers.filter(n => n % 2 === 0); // number[]と推論
// reduceの結果も型推論される(初期値から推論)
const sum = numbers.reduce((acc, n) => acc + n, 0); // numberと推論
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(evens); // [2, 4]
console.log(sum); // 15
|
よくある間違いと対処法#
大文字と小文字の混同#
1
2
3
4
5
6
7
8
9
|
// 誤り: 大文字のラッパーオブジェクト型を使用
const name: String = "Taro"; // 非推奨
const age: Number = 25; // 非推奨
const flag: Boolean = true; // 非推奨
// 正しい: 小文字のプリミティブ型を使用
const name2: string = "Taro"; // 推奨
const age2: number = 25; // 推奨
const flag2: boolean = true; // 推奨
|
anyの乱用#
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 誤り: anyを乱用する
function process(data: any): any {
return data.value; // 実行時エラーの可能性
}
// 正しい: 適切な型を定義する
interface DataWithValue {
value: string;
}
function processSafe(data: DataWithValue): string {
return data.value; // 型安全
}
|
nullチェックの省略#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 誤り: nullチェックを省略
function getLength(str: string | null): number {
return str.length; // エラー: Object is possibly 'null'
}
// 正しい: nullチェックを行う
function getLengthSafe(str: string | null): number {
if (str === null) {
return 0;
}
return str.length;
}
// または、オプショナルチェーンとNullish coalescingを使用
function getLengthModern(str: string | null): number {
return str?.length ?? 0;
}
|
まとめ#
本記事では、TypeScriptの基本型について詳しく解説しました。
学んだ内容を振り返ります。
- プリミティブ型: string、number、boolean、null、undefined、symbol、bigintの7種類がある
- 型注釈: 変数や関数の引数・戻り値に
:を使って型を明示する
- 型推論: TypeScriptが自動的に型を推測するため、多くの場合は型注釈を省略できる
- any型とunknown型: anyは型チェックを無効化し、unknownは型ガードを必須とする安全な型
TypeScriptの型システムを正しく理解し活用することで、バグの少ない堅牢なコードを書けるようになります。次のステップとして、配列・タプル型やオブジェクト型について学ぶことで、より複雑なデータ構造も型安全に扱えるようになります。
参考リンク#