はじめに

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ではintfloatといった区別はありません。

 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では、nullundefinedはそれぞれ独立した型として扱われます。

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

nullundefinedの使い分けには諸説ありますが、一般的には以下のように使い分けられます。

意味 使用シーン
null 意図的に「値がない」ことを表す データベースの空の値、意図的な初期化
undefined 未定義、値が設定されていない 未初期化の変数、省略されたオプション引数

TypeScriptのstrictNullChecksオプション(推奨設定)を有効にすると、nullundefinedを他の型に代入できなくなり、より安全なコードが書けます。

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.jsontargetオプションを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には、どんな型の値でも受け入れる特殊な型としてanyunknownがあります。両者は似ているようで、安全性に大きな違いがあります。

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.jsonnoImplicitAnyオプション(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の型システムを正しく理解し活用することで、バグの少ない堅牢なコードを書けるようになります。次のステップとして、配列・タプル型やオブジェクト型について学ぶことで、より複雑なデータ構造も型安全に扱えるようになります。

参考リンク