TypeScript

TypeScript入門

型が教えてくれる情報を読み、間違いを実行前に見つけられるコードを書きましょう。

プログラミングが初めてでも試せる短い例から始めます。

01

TypeScriptとは

TypeScriptはJavaScriptへ静的な型チェックを加えた言語です。.tsファイルをコンパイラで検査し、JavaScriptへ変換して実行します。型は開発中の間違いを見つける助けになりますが、実行時には消えます。

役割TypeScriptJavaScript
開発時型を検査し、入力補完を助ける実行する処理を記述する
実行時型注釈は残らないブラウザやNode.jsが実行する
型は値を変換しない const age: number = Number("20")のように、外部の文字列は実際の処理で数値へ変換します。型注釈を書くだけでは値は変わりません。
02

練習環境を作る

Node.jsとnpmを導入済みの環境で、練習用ディレクトリへTypeScriptを開発用依存として入れます。プロジェクトごとに版を固定すると、別のプロジェクトへ影響しません。

プロジェクトを準備COMMAND
mkdir typescript-practice
cd typescript-practice
npm init -y
npm install --save-dev typescript
npx tsc --init
版を確認COMMAND
node --version
npx tsc --version
表示例OUTPUT
v22.0.0
Version 5.x.x

版番号は環境で異なります。生成されたtsconfig.jsonではstrictを有効にし、型の見落としを減らします。

コマンドが見つからない場合 Node.jsの導入とPATHを確認します。管理者権限でグローバルインストールせず、まずプロジェクト内のnpx tscを使いましょう。
03

最初のプログラムを動かす

hello.tsを作り、次のコードを保存します。

hello.tsTYPESCRIPT
const message: string = "こんにちは、TypeScript!";
console.log(message);
検査・変換・実行COMMAND
npx tsc hello.ts
node hello.js
実行結果OUTPUT
こんにちは、TypeScript!

tschello.jsを生成し、Node.jsがJavaScriptを実行します。文末のセミコロンは省略できますが、この講座では表記を揃えるため記述します。

04

変数、定数、基本の型

再代入しない値はconst、後で値を変える必要がある場合だけletを使います。varはスコープが異なり混乱しやすいため、新しいコードでは通常使いません。

基本の値TYPESCRIPT
const userName: string = "Mity";
let age: number = 20;
const isStudent: boolean = true;

age = 21;
console.log(`${userName}さんは${age}歳です`);
console.log(isStudent);
実行結果OUTPUT
Mityさんは21歳です
true

型推論

右辺から型が明らかな場合、TypeScriptは型を推論します。

推論される型TYPESCRIPT
const language = "TypeScript"; // stringと推論
let count = 3;                 // numberと推論

// count = "三"; // エラー: stringをnumberへ代入できない
console.log(language, count);
実行結果OUTPUT
TypeScript 3
型注釈を書きすぎない 初期値から明らかなローカル変数は推論に任せ、関数の引数や公開データの形など、境界では型を明示すると読みやすくなります。
05

演算子

種類演算子
計算+ - * / % **7 % 3は1
比較=== !== > < >= <=score >= 60
論理&& || !isMember && hasTicket
代入= += -= *= /=total += 100
条件条件 ? 真の場合 : 偽の場合age >= 18 ? "成人" : "未成年"
料金を計算TYPESCRIPT
const price = 500;
const count = 3;
const subtotal = price * count;
const tax = Math.floor(subtotal * 0.1);

console.log(`小計: ${subtotal}円`);
console.log(`合計: ${subtotal + tax}円`);
実行結果OUTPUT
小計: 1500円
合計: 1650円
==より=== ==は型変換を伴う比較をするため、予想外の一致が起こります。型と値の両方を比較する===を基本にします。
06

条件分岐

ifelse ifelseで処理を切り替えます。条件は上から判定され、最初に真になった処理だけが実行されます。

点数を判定TYPESCRIPT
const score = 82;
let result: string;

if (score >= 80) {
  result = "合格(よくできました)";
} else if (score >= 60) {
  result = "合格";
} else {
  result = "再挑戦";
}

console.log(result);
実行結果OUTPUT
合格(よくできました)

80点以上の条件を60点以上より前に置くことで、より具体的な条件を先に判定しています。

07

繰り返し

回数が決まっている処理にはfor、配列の値を順に使う場合はfor...ofが読みやすい書き方です。

回数と値を繰り返すTYPESCRIPT
for (let number = 1; number <= 3; number += 1) {
  console.log(`${number}回目`);
}

const colors = ["赤", "青"];
for (const color of colors) {
  console.log(color);
}
実行結果OUTPUT
1回目
2回目
3回目
赤
青
無限ループ whileを使う場合は、条件がいつ偽になるかを確認します。ブラウザやサーバーが応答しなくなる原因になります。
08

配列を扱う

string[]は文字列だけを入れられる配列です。位置は0から数えます。

追加・変換・抽出TYPESCRIPT
const scores: number[] = [40, 65, 80];
scores.push(95);

const passed = scores.filter((score) => score >= 60);
const labels = passed.map((score) => `${score}点`);

console.log(scores.length);
console.log(labels);
実行結果OUTPUT
4
[ '65点', '80点', '95点' ]

filterは条件に合う値を集め、mapは各値を別の値へ変換した新しい配列を作ります。元の配列は書き換えません。

添字の範囲 存在しない位置を読むと実行時にはundefinedになります。設定のnoUncheckedIndexedAccessを有効にすると、その可能性を型にも反映できます。
09

オブジェクトの形を型で表す

typeで、利用者データが持つプロパティの名前と型を定義します。?は値がない場合もあるプロパティです。

利用者を表示TYPESCRIPT
type User = {
  readonly id: number;
  name: string;
  email?: string;
};

const user: User = { id: 1, name: "Mity" };
console.log(`${user.id}: ${user.name}`);
console.log(user.email ?? "メール未登録");
実行結果OUTPUT
1: Mity
メール未登録

readonlyはTypeScriptの検査で再代入を防ぎます。??は左側がnullまたはundefinedのときだけ右側を使います。

10

関数へ型を付ける

引数には受け取れる型、戻り値には返す型を書きます。呼び出す側は必要な値を確認でき、関数内部では型に合う操作だけを使えます。

税込金額を返すTYPESCRIPT
function calculateTotal(price: number, count: number): number {
  const subtotal = price * count;
  return subtotal + Math.floor(subtotal * 0.1);
}

console.log(calculateTotal(500, 3));
// calculateTotal("500", 3); // 型エラー
実行結果OUTPUT
1650

値を返さない関数

voidTYPESCRIPT
function showMessage(message: string): void {
  console.log(message);
}

showMessage("処理が完了しました");
実行結果OUTPUT
処理が完了しました
11

複数の可能性をユニオン型で表す

|は「どちらか」の型です。利用前にtypeofで型を絞り込むと、それぞれに合う処理を書けます。

IDを文字列化TYPESCRIPT
function formatId(id: number | string): string {
  if (typeof id === "number") {
    return `ID-${id.toString().padStart(4, "0")}`;
  }
  return id.toUpperCase();
}

console.log(formatId(25));
console.log(formatId("guest"));
実行結果OUTPUT
ID-0025
GUEST

分岐の内側では、TypeScriptがidをそれぞれnumberstringとして扱います。これを型の絞り込み(ナローイング)と呼びます。

12

nullとundefinedを安全に扱う

strictNullChecksが有効なら、値がない可能性を無視できません。オプショナルチェーン?.は、左側がnullまたはundefinedのとき、それ以降へアクセスせずundefinedを返す構文です。??と組み合わせると既定値を用意できます。

プロフィールを表示TYPESCRIPT
type Profile = { address?: { city?: string } };

const profile: Profile = {};
const city = profile.address?.city ?? "未設定";

console.log(city);
実行結果OUTPUT
未設定

profile.addressundefinedなので、?..cityを読み取らずundefinedを返します。その結果、??の右側にある「未設定」が使われます。

non-nullアサーションを安易に使わない value!は、コンパイラに「この値はnullでもundefinedでもない」と開発者が宣言する構文です。型エラーを抑えるだけで、実行時に値の存在を確認しません。可能なら条件分岐や既定値で安全に処理します。
PRACTICE

練習問題:商品一覧を集計する

商品名、単価、個数を持つオブジェクトの配列を作り、各商品の小計と全商品の合計を表示してください。

解答例を見る
shop.tsTYPESCRIPT
type Product = {
  name: string;
  price: number;
  quantity: number;
};

const products: Product[] = [
  { name: "ノート", price: 200, quantity: 2 },
  { name: "ペン", price: 120, quantity: 3 },
];

let total = 0;
for (const product of products) {
  const subtotal = product.price * product.quantity;
  total += subtotal;
  console.log(`${product.name}: ${subtotal}円`);
}
console.log(`合計: ${total}円`);
実行結果OUTPUT
ノート: 400円
ペン: 360円
合計: 760円
NEXT

次に学ぶこと