10日で覚えるTypeScriptDay 2: 基本の型を理解する
books.chapter 210日で覚えるTypeScript

Day 2: 基本の型を理解する

今日学ぶこと

  • プリミティブ型(string, number, boolean)
  • 型注釈と型推論の違い
  • any, unknown, never の使い分け
  • null と undefined の扱い方

型とは何か

TypeScriptにおける「型」とは、値の集合と、その値に対して実行できる操作を表します。

例えば:

  • string型 = すべての文字列の集合 + 文字列に対する操作(+, .toUpperCase(), .slice()など)
  • number型 = すべての数値の集合 + 数値に対する操作(+, -, *, /など)

型を知ることで、その値に対して何ができるか何ができないかが明確になります。

flowchart TB
    subgraph Types["TypeScriptの型階層"]
        direction TB
        Any["any\n(何でもあり)"]
        Unknown["unknown\n(未知の型)"]

        subgraph Primitives["プリミティブ型"]
            String["string"]
            Number["number"]
            Boolean["boolean"]
            BigInt["bigint"]
            Symbol["symbol"]
        end

        subgraph Special["特殊な型"]
            Null["null"]
            Undefined["undefined"]
            Void["void"]
            Never["never\n(ありえない)"]
        end
    end

    Any --> Unknown
    Unknown --> Primitives
    Unknown --> Special

    style Types fill:#3178c6,color:#fff
    style Primitives fill:#22c55e,color:#fff
    style Special fill:#f59e0b,color:#fff

プリミティブ型

JavaScriptの7つのプリミティブ値には、それぞれ対応するTypeScriptの型があります。

string(文字列)

// 型注釈を明示
let greeting: string = "Hello, World!";

// 型推論(TypeScriptが自動で判断)
let name = "TypeScript"; // string型と推論される

// 文字列の操作
greeting.toUpperCase(); // OK
greeting.toLowerCase(); // OK
greeting * 2;           // エラー: 文字列に * 演算子は使えない

number(数値)

let age: number = 25;
let price = 19.99;        // number型と推論
let hex = 0xff;           // 16進数もnumber
let binary = 0b1010;      // 2進数もnumber

// 数値の操作
age + 10;        // OK
age.toFixed(2);  // OK
age.toUpperCase(); // エラー: numberにtoUpperCaseはない

boolean(真偽値)

let isActive: boolean = true;
let isCompleted = false;  // boolean型と推論

// 論理演算
!isActive;          // OK
isActive && isCompleted; // OK
isActive + 1;       // エラー: booleanに + 演算子は使えない

bigint(大きな整数)

// ES2020以降で利用可能
let bigNumber: bigint = 9007199254740991n;
let anotherBig = BigInt(123); // bigint型と推論

// numberとbigintは混在できない
let num: number = 100;
bigNumber + num; // エラー: bigintとnumberは混在不可
bigNumber + 100n; // OK

symbol(シンボル)

let sym1: symbol = Symbol("description");
let sym2 = Symbol("description");

// シンボルは常にユニーク
sym1 === sym2; // false(同じ説明でも異なるシンボル)

型注釈と型推論

TypeScriptには型を指定する2つの方法があります。

型注釈(Type Annotation)

プログラマーが明示的に型を指定します:

let username: string = "alice";
let count: number = 42;
let isValid: boolean = true;

型推論(Type Inference)

TypeScriptが初期値から自動的に型を推論します:

let username = "alice";    // string と推論
let count = 42;            // number と推論
let isValid = true;        // boolean と推論
flowchart LR
    subgraph Annotation["型注釈"]
        A1["let x: number = 5"]
        A2["明示的に型を指定"]
    end

    subgraph Inference["型推論"]
        I1["let x = 5"]
        I2["TypeScriptが推論"]
    end

    A1 --> R["number型"]
    I1 --> R

    style Annotation fill:#3b82f6,color:#fff
    style Inference fill:#22c55e,color:#fff

どちらを使うべき?

シナリオ 推奨
初期値がある変数 型推論に任せる
関数の引数 型注釈を使う
関数の戻り値 型注釈を使う(複雑な場合)
初期値がない変数 型注釈が必須
// 型推論で十分なケース
let message = "Hello"; // string と明らか

// 型注釈が必要なケース
let data: string[]; // 初期値なし、後で代入
data = ["a", "b", "c"];

// 関数の引数には型注釈
function greet(name: string): string {
  return `Hello, ${name}!`;
}

any型:逃げ道だが使うべきでない

any型は、どんな型でも受け入れる特殊な型です。

let anything: any = "hello";
anything = 42;        // OK
anything = true;      // OK
anything = { x: 1 };  // OK

// anyは型チェックを無効化
anything.foo.bar.baz(); // エラーにならない(実行時エラーの危険)

anyの問題点

flowchart TD
    A["any型を使用"] --> B["型チェックが無効化"]
    B --> C["コンパイルは通る"]
    C --> D["実行時エラー発生"]
    D --> E["TypeScriptの\nメリットが失われる"]

    style A fill:#ef4444,color:#fff
    style E fill:#ef4444,color:#fff

anyは原則として使わないことを推奨します。やむを得ない場合のみ使用してください。


unknown型:安全な「わからない」

unknown型は「型が分からない」ことを表しますが、anyより安全です。

let value: unknown = "hello";
value = 42;        // OK
value = true;      // OK

// unknownはそのままでは操作できない
value.toUpperCase(); // エラー: 'unknown'型にはtoUpperCaseがない

// 型を確認してから操作
if (typeof value === "string") {
  value.toUpperCase(); // OK: stringであることが確認された
}

anyとunknownの比較

特徴 any unknown
何でも代入できる
そのまま操作できる
型チェックの恩恵
推奨度
// 外部からのデータを受け取る場合
function processData(data: unknown) {
  // 型チェックが必須
  if (typeof data === "string") {
    return data.toUpperCase();
  }
  if (typeof data === "number") {
    return data * 2;
  }
  return null;
}

never型:決して起こらない

never型は、決して値を持たない型です。主に2つの場面で使われます:

1. 関数が決してreturnしない

// 常に例外をスロー
function throwError(message: string): never {
  throw new Error(message);
}

// 無限ループ
function infiniteLoop(): never {
  while (true) {
    // 永遠に終わらない
  }
}

2. 網羅性チェック

type Color = "red" | "green" | "blue";

function getColorCode(color: Color): string {
  switch (color) {
    case "red":
      return "#ff0000";
    case "green":
      return "#00ff00";
    case "blue":
      return "#0000ff";
    default:
      // ここに到達したら型エラー
      const exhaustiveCheck: never = color;
      return exhaustiveCheck;
  }
}

null と undefined

JavaScriptには「値がない」ことを表す2つの値があります。

let nullable: string | null = null;
let undefinedValue: string | undefined = undefined;

// nullableな値を使う前にチェック
if (nullable !== null) {
  console.log(nullable.toUpperCase()); // OK
}

strictNullChecks

tsconfig.jsonstrict: trueまたはstrictNullChecks: trueを設定すると、nullやundefinedの扱いが厳格になります。

// strictNullChecks: true の場合
let name: string = null; // エラー: nullはstringに代入できない

// nullを許容する場合は明示的に
let name: string | null = null; // OK
flowchart LR
    subgraph Without["strictNullChecks: false"]
        W1["string = null"] --> W2["OK(危険)"]
    end

    subgraph With["strictNullChecks: true"]
        S1["string = null"] --> S2["エラー"]
        S3["string | null = null"] --> S4["OK(安全)"]
    end

    style Without fill:#ef4444,color:#fff
    style With fill:#22c55e,color:#fff

型エラーの読み方

TypeScriptのエラーメッセージは情報量が多いです。読み方を覚えましょう。

let count: number = "hello";
// Type 'string' is not assignable to type 'number'.

エラーメッセージの構造:

  • Type 'X' - 実際に渡された値の型
  • is not assignable to - 代入できない
  • type 'Y' - 期待されている型
flowchart LR
    A["Type 'string'"] --> B["is not assignable to"]
    B --> C["type 'number'"]

    D["実際の値の型"] -.-> A
    E["代入できない"] -.-> B
    F["期待される型"] -.-> C

    style A fill:#ef4444,color:#fff
    style C fill:#22c55e,color:#fff

まとめ

説明
string 文字列 "hello", 'world'
number 数値 42, 3.14
boolean 真偽値 true, false
bigint 大きな整数 9007199254740991n
symbol ユニークな識別子 Symbol("id")
any 何でもあり(非推奨) -
unknown 未知の型(安全) -
never ありえない型 -
null 値がない null
undefined 未定義 undefined

重要ポイント

  1. 型推論を活用 - 明らかな場合は型注釈を省略できる
  2. anyは避ける - 型チェックの恩恵が失われる
  3. unknownを使う - 型が分からない場合の安全な選択
  4. strictNullChecksを有効に - null安全なコードを書く

練習問題

問題1: 型の特定

以下の変数にはどの型が推論されるでしょうか?

let a = "TypeScript";
let b = 3.14;
let c = true;
let d = null;
let e;

問題2: 型エラーの修正

以下のコードのエラーを修正してください。

let score: number = "100";
let isActive: boolean = 1;
let name: string = undefined;

チャレンジ問題

unknown型の値を受け取り、文字列なら大文字に、数値なら2倍にする関数processValueを作成してください。それ以外の型の場合はnullを返します。


参考リンク


次回予告: Day 3では「Union型と型の絞り込み」を学びます。複数の型を組み合わせる方法と、Type Guardによる型の絞り込みを理解しましょう。