これだけ覚えればOK!TypeScriptの基本構文をやさしく解説

TypeScript

「TypeScript(タイプスクリプト)を学びたいけど、どこから手をつければいいかわからない…」
そんな悩みを抱えていませんか?

JavaScript(ジャバスクリプト)を書いたことがあっても、型やインターフェースなど、TypeScript特有の書き方に戸惑う人は多いです。

この記事では、TypeScriptをこれから始めたい人に向けて、基本構文をわかりやすく解説します。

よく使う書き方だけをギュッとまとめたので、これを読めばすぐにTypeScriptを書き始められます。

スポンサーリンク

TypeScriptとは何か

TypeScriptの概要

TypeScriptはMicrosoft社が開発したJavaScriptを拡張したプログラミング言語です。

最大の特徴は「型(type)」を使えることで、開発中にミスを見つけやすくなり、大規模な開発でもバグが減ります。

JavaScriptとの違い

JavaScriptの問題点

// JavaScriptの例 - 実行時にエラーが起きる
function add(a, b) {
  return a + b;
}

console.log(add(5, 10));      // 15 (期待通り)
console.log(add("5", "10"));  // "510" (予期しない結果)

TypeScriptでの解決

// TypeScriptの例 - 開発時にエラーを検出
function add(a: number, b: number): number {
  return a + b;
}

console.log(add(5, 10));      // 15 (OK)
console.log(add("5", "10"));  // コンパイルエラー!

TypeScriptのメリット

開発時のエラー検出

  • タイプミスの発見:プロパティ名の間違いなど
  • 型の不一致:数値と文字列の混同など
  • 存在しないメソッドの呼び出し:APIの変更による問題など

開発効率の向上

  • エディタの支援:自動補完、リファクタリング機能
  • ドキュメント化:型情報が仕様書の役割
  • チーム開発:コードの意図が明確になる

開発環境の準備

Node.jsとnpmのインストール

TypeScriptを使うには、まずNode.jsが必要です。

インストール確認

node --version
npm --version

TypeScriptのインストール

グローバルインストール

npm install -g typescript

プロジェクト単位でのインストール

npm init -y
npm install --save-dev typescript

TypeScriptの実行方法

コンパイルして実行

# TypeScriptファイルをJavaScriptに変換
tsc sample.ts

# 生成されたJavaScriptファイルを実行
node sample.js

ts-nodeを使った直接実行

# ts-nodeをインストール
npm install -g ts-node

# TypeScriptファイルを直接実行
ts-node sample.ts

基本的な型の使い方

プリミティブ型

TypeScriptには以下の基本的な型があります。

数値型(number)

let age: number = 25;
let price: number = 1299.99;
let hexValue: number = 0xFF;
let binaryValue: number = 0b1010;

文字列型(string)

let userName: string = "田中太郎";
let message: string = `こんにちは、${userName}さん`;
let multiLine: string = `
  これは複数行の
  文字列です
`;

真偽値型(boolean)

let isComplete: boolean = true;
let isVisible: boolean = false;
let isLoggedIn: boolean = age >= 18;

null と undefined

let emptyValue: null = null;
let notDefined: undefined = undefined;

// strictNullChecksオプションで制御可能
let maybeString: string | null = null;

型推論の活用

TypeScriptは初期値から型を自動的に推論してくれます。

基本的な型推論

// 型注釈なしでも型が決まる
let autoNumber = 42;        // number型
let autoString = "Hello";   // string型
let autoBool = true;        // boolean型

// 型を確認(エディタで確認可能)
// autoNumber = "文字列";    // エラー:number型に文字列は代入不可

複雑な型推論

// 配列の型推論
let numbers = [1, 2, 3];           // number[]型
let mixed = [1, "hello", true];    // (string | number | boolean)[]型

// 関数の戻り値型推論
function getFullName(first: string, last: string) {
  return `${first} ${last}`;  // 戻り値はstring型と推論される
}

変数の宣言と型注釈

基本的な変数宣言

let と const の使い分け

// 値が変わる可能性がある場合はlet
let score: number = 0;
score = 100;  // OK

// 値が変わらない場合はconst
const PI: number = 3.14159;
// PI = 3.14;  // エラー:constは再代入不可

型注釈の書き方

// 基本形:変数名: 型 = 値
let userName: string = "田中太郎";
let userAge: number = 30;
let isActive: boolean = true;

// 初期値なしの場合
let futureValue: string;
futureValue = "後で設定される値";

配列の型

基本的な配列の型

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

// 文字列の配列
let fruits: string[] = ["りんご", "バナナ", "オレンジ"];

// 真偽値の配列
let flags: boolean[] = [true, false, true];

Array<T>記法

// 以下は上記と同じ意味
let numbers: Array<number> = [1, 2, 3];
let fruits: Array<string> = ["りんご", "バナナ"];

混合型の配列

// ユニオン型を使った混合配列
let mixed: (string | number)[] = [1, "hello", 2, "world"];

// 別の書き方
let mixed2: Array<string | number> = [1, "hello", 2];

オブジェクトの型

インライン型注釈

let user: { name: string; age: number; email: string } = {
  name: "田中太郎",
  age: 30,
  email: "tanaka@example.com"
};

オプショナルプロパティ

let user: { 
  name: string; 
  age: number; 
  email?: string;  // ?をつけるとオプショナル(省略可能)
} = {
  name: "佐藤花子",
  age: 25
  // emailは省略可能
};

読み取り専用プロパティ

let config: { 
  readonly apiUrl: string; 
  readonly timeout: number; 
} = {
  apiUrl: "https://api.example.com",
  timeout: 5000
};

// config.apiUrl = "新しいURL";  // エラー:読み取り専用

関数の型

基本的な関数の型

通常の関数宣言

function greet(name: string): string {
  return `こんにちは、${name}さん`;
}

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

function logMessage(message: string): void {
  console.log(message);
  // voidは戻り値がないことを示す
}

アロー関数

const greet = (name: string): string => {
  return `こんにちは、${name}さん`;
};

// 短縮形
const add = (a: number, b: number): number => a + b;

// 戻り値がない場合
const logMessage = (message: string): void => {
  console.log(message);
};

関数のパラメータ

デフォルトパラメータ

function greet(name: string, greeting: string = "こんにちは"): string {
  return `${greeting}、${name}さん`;
}

console.log(greet("田中"));              // "こんにちは、田中さん"
console.log(greet("田中", "おはよう"));   // "おはよう、田中さん"

オプショナルパラメータ

function buildName(firstName: string, lastName?: string): string {
  if (lastName) {
    return `${firstName} ${lastName}`;
  }
  return firstName;
}

console.log(buildName("太郎"));           // "太郎"
console.log(buildName("太郎", "田中"));   // "太郎 田中"

レストパラメータ

function sum(...numbers: number[]): number {
  return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3));        // 6
console.log(sum(1, 2, 3, 4, 5));  // 15

関数型の変数

関数を変数に代入

// 関数型の定義
let mathOperation: (x: number, y: number) => number;

// 関数を代入
mathOperation = (a, b) => a + b;
console.log(mathOperation(5, 3));  // 8

mathOperation = (a, b) => a * b;
console.log(mathOperation(5, 3));  // 15

コールバック関数

function processArray(
  items: number[], 
  callback: (item: number) => number
): number[] {
  return items.map(callback);
}

const numbers = [1, 2, 3, 4, 5];
const doubled = processArray(numbers, x => x * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

インターフェース(Interface)

基本的なインターフェース

オブジェクトの型定義

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

let user: User = {
  id: 1,
  name: "田中太郎",
  email: "tanaka@example.com",
  age: 30
};

オプショナルプロパティ

interface Product {
  id: number;
  name: string;
  price: number;
  description?: string;  // オプショナル
  category?: string;     // オプショナル
}

let product: Product = {
  id: 1,
  name: "ノートPC",
  price: 98000
  // descriptionとcategoryは省略可能
};

読み取り専用プロパティ

interface Config {
  readonly apiUrl: string;
  readonly version: string;
  timeout: number;  // 変更可能
}

let config: Config = {
  apiUrl: "https://api.example.com",
  version: "1.0.0",
  timeout: 5000
};

config.timeout = 10000;      // OK
// config.apiUrl = "新URL";  // エラー:読み取り専用

関数のインターフェース

関数型の定義

interface Calculator {
  (x: number, y: number): number;
}

let add: Calculator = (a, b) => a + b;
let multiply: Calculator = (a, b) => a * b;

console.log(add(5, 3));       // 8
console.log(multiply(5, 3));  // 15

メソッドを持つインターフェース

interface UserService {
  getUser(id: number): User;
  createUser(userData: Partial<User>): User;
  updateUser(id: number, userData: Partial<User>): User;
  deleteUser(id: number): boolean;
}

class UserManager implements UserService {
  getUser(id: number): User {
    // 実装内容
    return {
      id: id,
      name: "サンプルユーザー",
      email: "user@example.com",
      age: 25
    };
  }

  createUser(userData: Partial<User>): User {
    // 実装内容
    return {
      id: Date.now(),
      name: userData.name || "無名ユーザー",
      email: userData.email || "",
      age: userData.age || 0
    };
  }

  updateUser(id: number, userData: Partial<User>): User {
    // 実装内容
    return this.getUser(id);
  }

  deleteUser(id: number): boolean {
    // 実装内容
    return true;
  }
}

インターフェースの継承

基本的な継承

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

interface Dog extends Animal {
  breed: string;
  bark(): void;
}

let myDog: Dog = {
  name: "ポチ",
  age: 3,
  breed: "柴犬",
  bark() {
    console.log("ワンワン!");
  }
};

複数インターフェースの継承

interface Flyable {
  fly(): void;
}

interface Swimmable {
  swim(): void;
}

interface Duck extends Animal, Flyable, Swimmable {
  quack(): void;
}

let duck: Duck = {
  name: "ダック",
  age: 2,
  fly() {
    console.log("空を飛んでいます");
  },
  swim() {
    console.log("水を泳いでいます");
  },
  quack() {
    console.log("クワクワ!");
  }
};

ユニオン型とリテラル型

ユニオン型

基本的なユニオン型

// string または number を受け取る
let value: string | number;

value = "Hello";   // OK
value = 42;        // OK
// value = true;   // エラー:boolean は含まれていない

関数でのユニオン型

function formatValue(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase();
  } else {
    return value.toString();
  }
}

console.log(formatValue("hello"));  // "HELLO"
console.log(formatValue(42));       // "42"

リテラル型

文字列リテラル型

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

let currentStatus: Status = "loading";
currentStatus = "success";  // OK
// currentStatus = "failed"; // エラー:定義されていない値

数値リテラル型

type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

function rollDice(): DiceRoll {
  return Math.floor(Math.random() * 6) + 1 as DiceRoll;
}

真偽値リテラル型

type Success = true;
type Failure = false;

let result: Success | Failure;
result = true;   // OK
result = false;  // OK

Type エイリアス

基本的なType エイリアス

複雑な型に名前をつける

type UserID = number;
type UserName = string;
type UserEmail = string;

type User = {
  id: UserID;
  name: UserName;
  email: UserEmail;
  createdAt: Date;
};

let user: User = {
  id: 1,
  name: "田中太郎",
  email: "tanaka@example.com",
  createdAt: new Date()
};

ユニオン型のエイリアス

type Theme = "light" | "dark" | "auto";
type Size = "small" | "medium" | "large";

type ButtonProps = {
  text: string;
  theme: Theme;
  size: Size;
  onClick: () => void;
};

Interface と Type の使い分け

Interfaceが適している場面

// オブジェクトの型定義
interface User {
  id: number;
  name: string;
}

// 継承が必要な場合
interface AdminUser extends User {
  permissions: string[];
}

Typeが適している場面

// ユニオン型
type Status = "loading" | "success" | "error";

// 関数の型
type EventHandler = (event: Event) => void;

// 複雑な型の組み合わせ
type ApiResponse<T> = {
  data: T;
  status: Status;
  message?: string;
};

実践的な例

シンプルなTodoアプリ

// 型定義
interface Todo {
  id: number;
  title: string;
  completed: boolean;
  createdAt: Date;
}

type TodoFilter = "all" | "active" | "completed";

class TodoManager {
  private todos: Todo[] = [];
  private nextId: number = 1;

  addTodo(title: string): Todo {
    const newTodo: Todo = {
      id: this.nextId++,
      title: title,
      completed: false,
      createdAt: new Date()
    };
    
    this.todos.push(newTodo);
    return newTodo;
  }

  toggleTodo(id: number): boolean {
    const todo = this.todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
      return true;
    }
    return false;
  }

  getTodos(filter: TodoFilter = "all"): Todo[] {
    switch (filter) {
      case "active":
        return this.todos.filter(t => !t.completed);
      case "completed":
        return this.todos.filter(t => t.completed);
      default:
        return this.todos;
    }
  }

  removeTodo(id: number): boolean {
    const index = this.todos.findIndex(t => t.id === id);
    if (index !== -1) {
      this.todos.splice(index, 1);
      return true;
    }
    return false;
  }
}

// 使用例
const todoManager = new TodoManager();

todoManager.addTodo("TypeScriptを学ぶ");
todoManager.addTodo("ブログを書く");
todoManager.toggleTodo(1);

console.log(todoManager.getTodos("active"));

ユーザー管理システム

// 型定義
interface BaseUser {
  id: number;
  name: string;
  email: string;
}

interface RegularUser extends BaseUser {
  role: "user";
  lastLogin?: Date;
}

interface AdminUser extends BaseUser {
  role: "admin";
  permissions: string[];
}

type User = RegularUser | AdminUser;

// ユーザー管理クラス
class UserManager {
  private users: User[] = [];

  createRegularUser(name: string, email: string): RegularUser {
    const user: RegularUser = {
      id: Date.now(),
      name,
      email,
      role: "user"
    };
    
    this.users.push(user);
    return user;
  }

  createAdminUser(name: string, email: string, permissions: string[]): AdminUser {
    const user: AdminUser = {
      id: Date.now(),
      name,
      email,
      role: "admin",
      permissions
    };
    
    this.users.push(user);
    return user;
  }

  getUser(id: number): User | undefined {
    return this.users.find(user => user.id === id);
  }

  isAdmin(user: User): user is AdminUser {
    return user.role === "admin";
  }

  hasPermission(user: User, permission: string): boolean {
    if (this.isAdmin(user)) {
      return user.permissions.includes(permission);
    }
    return false;
  }
}

よくあるエラーと対処法

Type エラーの読み方

型の不一致エラー

// エラー例
let message: string = 123;
// Type 'number' is not assignable to type 'string'.

// 解決方法
let message: string = "123";           // 文字列にする
let messageNumber: number = 123;       // 型を変更する
let messageAny: string = String(123);  // 型変換する

プロパティが存在しないエラー

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

let user: User = { name: "太郎", age: 30 };

// エラー例
console.log(user.email);
// Property 'email' does not exist on type 'User'.

// 解決方法1:インターフェースを拡張
interface ExtendedUser {
  name: string;
  age: number;
  email?: string;
}

// 解決方法2:型アサーション(注意して使用)
console.log((user as any).email);

null/undefined エラー

strict モードでのエラー

// エラー例
function greet(name: string | null) {
  return `Hello, ${name.toUpperCase()}`;
  // Object is possibly 'null'.
}

// 解決方法1:null チェック
function greet(name: string | null) {
  if (name === null) {
    return "Hello, Guest";
  }
  return `Hello, ${name.toUpperCase()}`;
}

// 解決方法2:デフォルト値
function greet(name: string | null) {
  const safeName = name || "Guest";
  return `Hello, ${safeName.toUpperCase()}`;
}

開発環境の設定

tsconfig.json の基本設定

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020", "DOM"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

エディタの設定

VS Code での設定

// .vscode/settings.json
{
  "typescript.preferences.importModuleSpecifier": "relative",
  "typescript.suggest.autoImports": true,
  "typescript.updateImportsOnFileMove.enabled": "always",
  "editor.codeActionsOnSave": {
    "source.organizeImports": true
  }
}

まとめ

TypeScriptの基本構文まとめ

今回紹介したTypeScriptの基本構文をまとめると:

型の基本

  1. プリミティブ型numberstringboolean
  2. 配列型number[]Array<string>
  3. オブジェクト型{ name: string; age: number }
  4. ユニオン型string | number
  5. リテラル型"success" | "error"

関数の型

  1. パラメータの型(name: string) => void
  2. 戻り値の型(): string
  3. オプショナルパラメータ(name?: string)
  4. デフォルトパラメータ(name: string = "Guest")

インターフェース

  1. 基本定義interface User { name: string }
  2. 継承interface Admin extends User
  3. オプショナルemail?: string
  4. 読み取り専用readonly id: number

Type エイリアス

  1. 型の別名type UserID = number
  2. ユニオン型type Status = "loading" | "success"
  3. 複雑な型type ApiResponse<T> = { data: T }

コメント

タイトルとURLをコピーしました