タイトル: 文法
SEOタイトル: TypeScript 基本文法完全ガイド
| この記事の要点 |
|
変数宣言と型注釈
// let / const / var (var は非推奨)
let count: number = 0;
const PI: number = 3.14;
let name: string = "山田";
let active: boolean = true;
// 配列
let nums: number[] = [1, 2, 3];
let strs: Array<string> = ["a", "b"];
// タプル
let pair: [string, number] = ["age", 30];
// オブジェクト
let user: { name: string; age: number } = { name: "山田", age: 30 };
// 関数
let add: (a: number, b: number) => number = (a, b) => a + b;
関数の型
// 関数宣言
function greet(name: string, age?: number): string {
return age ? `${name} (${age})` : name;
}
// アロー関数
const double = (x: number): number => x * 2;
// 戻り値型を推論させる
function double2(x: number) {
return x * 2; // : number と推論
}
// void / never
function log(msg: string): void {
console.log(msg);
}
function fail(msg: string): never {
throw new Error(msg); // 戻ってこない
}
// オプショナル / デフォルト引数
function f(a: number, b?: number, c: number = 10) {}
// → b?: number means b: number | undefined
// Rest パラメータ
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
// 関数型を type で定義
type BinaryOp = (a: number, b: number) => number;
const mul: BinaryOp = (a, b) => a * b;
interface vs type
| 機能 | interface | type |
|---|---|---|
| オブジェクト型定義 | ○ | ○ |
| extends / implements | ○ | △ (& で代替) |
| 宣言マージ (重複定義) | ○ | × |
| Union / Intersection | × | ○ |
| Mapped / Conditional | × | ○ |
| Primitive を別名化 | × | ○ |
// interface (オブジェクト指向風)
interface User {
id: number;
name: string;
}
interface User { email: string; } // ★ マージできる
interface Admin extends User { role: string; }
// type (関数型風、より柔軟)
type ID = string | number; // ← interface ではできない
type Point = { x: number; y: number };
type Line = { start: Point; end: Point };
// Intersection
type Employee = User & { salary: number };
// 大抵 type で OK。ライブラリ公開 API は interface 推奨 (拡張できるため)
Optional と Nullable
// Optional (プロパティ自体が無くても OK)
interface Config {
host: string;
port?: number; // number | undefined
}
// Nullable (値が null になる可能性)
interface User {
name: string;
email: string | null;
}
// strictNullChecks 下では undefined と null は別物
let a: string = null; // ❌ エラー
let b: string | null = null; // ✅
// Optional chaining と Nullish coalescing
const user: User | undefined = getUser();
const len = user?.name?.length ?? 0;
Union / Intersection / Literal Types
// Union
type Status = "active" | "inactive" | "pending";
let s: Status = "active"; // ✅
let s2: Status = "running"; // ❌
// Intersection
type A = { a: number };
type B = { b: string };
type AB = A & B; // { a: number; b: string }
// Numeric Literal
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
// Template Literal Type (TS 4.1+)
type Greeting = `Hello, ${string}`; // "Hello, ..." 形式
let g: Greeting = "Hello, world";
unknown / any / never
| 型 | 意味 | 使いどころ |
|---|---|---|
any | 何でも入る (型チェック無効) | 移行期のみ、新規禁止 |
unknown | 何でも入るが使う前に型を絞る必要あり | any の代わりに推奨 |
never | 絶対に発生しない型 | throw する関数、Exhaustive check |
void | 戻り値がないこと | 関数の戻り値型 |
function parseJson(s: string): unknown {
return JSON.parse(s);
}
const data = parseJson('{"name":"taro"}');
// data.name; // ❌ unknown なのでアクセス不可
// 型ガードで絞る
if (typeof data === "object" && data !== null && "name" in data) {
console.log((data as { name: string }).name);
}
// 型述語関数
function isUser(x: unknown): x is User {
return typeof x === "object" && x !== null && "id" in x;
}
if (isUser(data)) {
data.id; // ✅ User として扱える
}
as 型アサーション
// 開発者が「絶対この型」と保証する場合
const el = document.getElementById("app") as HTMLDivElement;
// const アサーション (リテラル型として固定)
const obj = { a: 1, b: 2 } as const;
// → { readonly a: 1; readonly b: 2 }
// 危険な多段アサーション (非推奨)
const x = (val as unknown) as number;
Generic (再掲)
function wrap<T>(value: T): { value: T } {
return { value };
}
wrap<number>(1); // { value: number }
wrap("hi"); // { value: string } と推論
// 制約付き Generic
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "taro", age: 30 };
getProp(user, "name"); // string
getProp(user, "xxx"); // ❌ "xxx" は keyof でない
Index Signature
// 任意のキー名で値を持つ
type Dict = { [key: string]: number };
const d: Dict = { a: 1, b: 2 };
// Record でも書ける
type Dict2 = Record<string, number>;
Mapped Type
type User = { id: number; name: string; email: string };
// 全部 readonly に
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type ReadOnlyUser = Readonly<User>;
// 全部 Optional に
type Partial<T> = { [K in keyof T]?: T[K] };
type PartialUser = Partial<User>;
// プロパティ名を変える (Key Remapping, TS 4.1+)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
type UserGetters = Getters<User>;
// → { getId(): number; getName(): string; getEmail(): string }
keyof / typeof
// keyof: オブジェクト型のキー全部を Union で取得
type User = { id: number; name: string };
type UserKeys = keyof User; // "id" | "name"
// typeof: 値から型を取得
const config = { host: "localhost", port: 3000 };
type Config = typeof config;
// → { host: string; port: number }
// 組み合わせ
const colors = { red: "#f00", green: "#0f0", blue: "#00f" } as const;
type ColorName = keyof typeof colors; // "red" | "green" | "blue"
type ColorHex = typeof colors[ColorName]; // "#f00" | "#0f0" | "#00f"
Conditional Type
// 型レベルの if-else
type IsString<T> = T extends string ? true : false;
type A = IsString<"hi">; // true
type B = IsString<42>; // false
// 配列要素型を取り出す
type ElementType<T> = T extends (infer U)[] ? U : T;
type X = ElementType<number[]>; // number
type Y = ElementType<string>; // string (配列でなければそのまま)
// 関数の戻り値型を取り出す (組み込み ReturnType と同じ)
type ReturnTypeOf<T> = T extends (...args: any) => infer R ? R : never;
クラスの文法
class Animal {
// public (省略可) / private / protected / readonly
constructor(
public readonly name: string,
private age: number,
) {}
greet(): string {
return `${this.name} (${this.age})`;
}
// getter / setter
get displayName() { return this.name.toUpperCase(); }
}
class Dog extends Animal {
bark() { console.log("Woof"); }
}
// 抽象クラス
abstract class Shape {
abstract area(): number;
}
class Circle extends Shape {
constructor(public r: number) { super(); }
area() { return Math.PI * this.r ** 2; }
}
FAQ
Q: interface と type、新規はどっち?
A: 個人開発・チームコードは type、公開 API は interface が一般的。Airbnb スタイルガイド等は interface 推奨。
Q: 型情報を文字列に変換できる?
A: 不可。型はコンパイル時にしか存在しません。実行時の判定は typeof/instanceof/型述語関数で。
Q: as と <Type> どっちが推奨?
A: as。<Type>val 記法は JSX (.tsx) と競合するため非推奨です。