TypeScriptとは?
TypeScriptは、JavaScriptに静的型付けを追加したプログラミング言語です。 コンパイル時に型エラーを検出することで、実行前にバグを発見し、大規模なアプリケーション開発を安全かつ効率的に進めることができます。
型安全
JavaScriptスーパーセット
エディタ補完強化
基本的な型
プリミティブ型は変数宣言時に型注釈で指定
// プリミティブ型
let age: number = 25;
let name: string = "太郎";
let isActive: boolean = true;
// 配列
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["太郎", "花子"];
// タプル
let user: [string, number] = ["太郎", 25];
// any (型安全を失うため非推奨)
let data: any = "文字列でも数値でもOK";
// unknown (anyより安全)
let value: unknown = "型チェック必須";
if (typeof value === "string") {
console.log(value.toUpperCase());
}
インターフェース
// 基本的なインターフェース
interface User {
id: number;
name: string;
email: string;
}
const user: User = {
id: 1,
name: "太郎",
email: "taro@example.com"
};
// オプショナルプロパティ
interface Product {
id: number;
name: string;
price: number;
description?: string; // 省略可能
}
// 読み取り専用プロパティ
interface Config {
readonly apiKey: string;
readonly endpoint: string;
}
const config: Config = {
apiKey: "abc123",
endpoint: "https://api.example.com"
};
// config.apiKey = "xyz"; // エラー!
型エイリアス
// プリミティブのエイリアス
type ID = number;
type Email = string;
// オブジェクト型
type User = {
id: ID;
email: Email;
createdAt: Date;
};
// ユニオン型
type Status = "pending" | "approved" | "rejected";
type Result = string | number | boolean;
// 関数型
type Callback = (data: string) => void;
type Calculator = (a: number, b: number) => number;
const add: Calculator = (a, b) => a + b;
● インターフェースと似ているが、ユニオン型などに使える
● プリミティブや関数の型定義に便利
ユニオン型と型ガード
// ユニオン型
type Response = string | number | null;
function handleResponse(res: Response) {
// 型ガード (typeof)
if (typeof res === "string") {
console.log(res.toUpperCase());
} else if (typeof res === "number") {
console.log(res.toFixed(2));
} else {
console.log("null response");
}
}
// in演算子による型ガード
type Cat = { meow: () => void };
type Dog = { bark: () => void };
function speak(animal: Cat | Dog) {
if ("meow" in animal) {
animal.meow();
} else {
animal.bark();
}
}
// カスタム型ガード
function isString(value: unknown): value is string {
return typeof value === "string";
}
関数の型
// 基本的な関数
function greet(name: string): string {
return `Hello, ${name}!`;
}
// オプション引数
function buildName(firstName: string, lastName?: string): string {
return lastName ? `${firstName} ${lastName}` : firstName;
}
// デフォルト引数
function multiply(a: number, b: number = 1): number {
return a * b;
}
// Rest引数
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
// アロー関数
const divide = (a: number, b: number): number => a / b;
// void型 (返り値なし)
function logMessage(message: string): void {
console.log(message);
}
// never型 (決して返らない)
function throwError(message: string): never {
throw new Error(message);
}
ジェネリクス
型を引数として受け取り、汎用的な処理を実現
// 基本的なジェネリクス関数
function identity<T>(arg: T): T {
return arg;
}
const result1 = identity<string>("hello");
const result2 = identity<number>(42);
// ジェネリクス配列
function getFirstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
// ジェネリクスインターフェース
interface Box<T> {
value: T;
}
const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 123 };
// 複数の型パラメータ
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
const userPair = pair("id", 1); // [string, number]
// ジェネリクス制約
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): void {
console.log(arg.length);
}
logLength("hello"); // OK
logLength([1, 2, 3]); // OK
// logLength(123); // エラー!
クラス
class Person {
// プロパティ
private name: string;
protected age: number;
public email: string;
// コンストラクタ
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
}
// メソッド
greet(): string {
return `Hello, I'm ${this.name}`;
}
// Getter
get fullInfo(): string {
return `${this.name} (${this.age})`;
}
// Setter
set updateAge(newAge: number) {
if (newAge > 0) {
this.age = newAge;
}
}
}
// 継承
class Employee extends Person {
private department: string;
constructor(name: string, age: number, email: string, dept: string) {
super(name, age, email);
this.department = dept;
}
getDepartment(): string {
return this.department;
}
}
// 抽象クラス
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}
ユーティリティ型
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial - すべてのプロパティをオプションに
type PartialUser = Partial<User>;
// { id?: number; name?: string; ... }
// Required - すべてのプロパティを必須に
type RequiredUser = Required<PartialUser>;
// Readonly - すべてのプロパティを読み取り専用に
type ReadonlyUser = Readonly<User>;
// Pick - 特定のプロパティのみ抽出
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string; }
// Omit - 特定のプロパティを除外
type UserWithoutEmail = Omit<User, "email">;
// { id: number; name: string; age: number; }
// Record - キーと値の型を指定
type UserRoles = Record<string, string>;
const roles: UserRoles = {
admin: "管理者",
user: "一般ユーザー"
};
// ReturnType - 関数の戻り値の型を取得
function getUser() {
return { id: 1, name: "太郎" };
}
type UserReturn = ReturnType<typeof getUser>;
// { id: number; name: string; }
非同期処理の型定義
Promise
// Promise型
function fetchUser(id: number): Promise<User> {
return fetch(`/api/users/${id}`)
.then(res => res.json());
}
// 使用例
fetchUser(1).then((user: User) => {
console.log(user.name);
});
async/await
// async関数
async function getUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const user: User = await response.json();
return user;
}
// エラーハンドリング
async function safeGetUser(id: number): Promise<User | null> {
try {
return await getUser(id);
} catch (error) {
console.error(error);
return null;
}
}
実務で役立つTips
厳格モード
tsconfig.jsonで"strict": trueを設定し、型チェックを最大限活用しましょう。
anyを避ける
anyは型安全性を失うため、unknownやジェネリクスを使いましょう。
型推論を活用
明示的な型注釈が不要な場合は、TypeScriptの型推論に任せることでコードを簡潔に保てます。
型ガード活用
ユニオン型を扱う際は、型ガードで型を絞り込み、安全にアクセスしましょう。
tsconfig.json 基本設定
{
"compilerOptions": {
"target": "ES2020", // コンパイル先のJavaScriptバージョン
"module": "ESNext", // モジュールシステム
"lib": ["ES2020", "DOM"], // 利用可能なライブラリ
"outDir": "./dist", // 出力ディレクトリ
"rootDir": "./src", // ソースディレクトリ
"strict": true, // 厳格な型チェック有効化
"esModuleInterop": true, // CommonJSとESモジュールの互換性
"skipLibCheck": true, // 型定義ファイルのチェックスキップ
"forceConsistentCasingInFileNames": true, // ファイル名の大文字小文字を厳格にチェック
"resolveJsonModule": true, // JSONファイルのインポートを許可
"moduleResolution": "node", // Node.js形式のモジュール解決
"declaration": true, // .d.tsファイルを生成
"sourceMap": true // ソースマップを生成
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
strict
すべての厳格な型チェックを有効化
target
コンパイル後のJSバージョン
module
モジュールシステム指定
よく使うパターン
APIレスポンスの型定義
interface ApiResponse<T> {
data: T;
status: number;
message?: string;
}
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser(id: number): Promise<ApiResponse<User>> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// 使用例
const result = await fetchUser(1);
console.log(result.data.name); // 型安全!
Enum (列挙型)
// 数値Enum
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 文字列Enum (推奨)
enum Status {
Pending = "PENDING",
Approved = "APPROVED",
Rejected = "REJECTED"
}
function updateStatus(status: Status) {
if (status === Status.Approved) {
console.log("承認されました");
}
}
// const enum (コンパイル後にインライン化)
const enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
型アサーション
// as構文 (推奨)
const input = document.getElementById("name") as HTMLInputElement;
input.value = "太郎";
// 山括弧構文 (JSXでは使えない)
const input2 = <HTMLInputElement>document.getElementById("email");
// 非nullアサーション演算子 (!)
const element = document.getElementById("app")!; // nullでないと確信
// constアサーション
const config = {
apiUrl: "https://api.example.com",
timeout: 5000
} as const;
// config.apiUrl = "..."; // エラー! 読み取り専用
交差型 (Intersection)
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: number;
department: string;
}
// 複数の型を結合
type EmployeePerson = Person & Employee;
const emp: EmployeePerson = {
name: "太郎",
age: 30,
employeeId: 1001,
department: "開発部"
};
コンパイルと実行
1
tsc --init
2
tsc
3
node dist/index.js
よく使うコマンド
# TypeScriptインストール
npm install -g typescript
# tsconfig.json生成
tsc --init
# 単一ファイルコンパイル
tsc index.ts
# プロジェクト全体コンパイル
tsc
# ウォッチモード (変更を監視)
tsc --watch
# バージョン確認
tsc --version
ts-nodeで直接実行
# ts-nodeインストール
npm install -g ts-node
# 直接実行 (コンパイル不要)
ts-node src/index.ts
# Node.jsプロジェクトの場合
npm install --save-dev typescript ts-node @types/node
# package.jsonのscripts
{
"scripts": {
"dev": "ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
}
}