TypeScript の型システム概要
TypeScript は JavaScript に静的型付け機能を付与した言語です。JavaScript のすべての機能が利用可能であるだけでなく、変数や関数の型を明示的に定義し、コンパイル時にエラーを検出できる仕組みが追加されています。
これにより、実行時ではなく開発段階で予期せぬ挙動を発見でき、コードの信頼性を高めることが可能です。
基本型と配列の定義
TypeScript の型は、JavaScript 由来のプリミティブ型と、TypeScript 独自で強化された型に大別されます。数値、文字列、真偽値などは JS と同様ですが、配列の表現方法には複数の記法があります。
配列型を定義する際は、要素の型の後に角括弧を付ける方法と、ジェネリック構文を使う方法があります。
const scores: number[] = [90, 85, 100];
const names: Array<string> = ['Alice', 'Bob', 'Charlie'];
複数の型が混在する配列を作成する場合は、聯合型(Union Types)を用います。縦棒(|)を用いて「A または B」であることを示します。
const mixedData: (number | string)[] = [1, 'text', 3, 'data'];
※ 聯合型を表す縦棒は 1 本であり、JavaScript の論理演算子 OR(||)とは異なる点に注意が必要です。
型エイリアスの活用
複雑な型定義を繰り返し使用する場合、型エイリアスを使用するとコード可読性が向上します。type キーワードを用いて任意の型に名前を付けることができます。
type IdList = (number | string)[];
const userIds: IdList = [101, 'guest_01', 102];
const adminIds: IdList = ['admin', 999];
これにより、型定義の変更が一箇所で済むため、保守性がが高まります。
関数の型定義
関数における型定義は、パラメータと戻り値に対して行います。関数宣言およびアロー関数のどちらでも型を指定可能です。
function calculateTotal(price: number, tax: number): number {
return price + tax;
}
const multiply = (a: number, b: number): number => {
return a * b;
};
関数そのものに変数型として型を付与する写法もあります。
const operate: (x: number, y: number) => number = (x, y) => x + y;
戻り値がない場合は void を使用します。また、パラメータが省略可能な場合は、変数名の後に问号(?)を付与します。ただし、必須パラメータの後に配置する必要があります。
function logMessage(message: string, prefix?: string): void {
console.log(prefix ? `${prefix}: ${message}` : message);
}
オブジェクト型とインターフェース
オブジェクトの形状を定義するには、インラインで型を記述するか、interface を使用します。
const user: { name: string; age: number; isActive: boolean } = {
name: 'Tanaka',
age: 25,
isActive: true
};
プロパティが省略可能な場合は、プロパティ名の後に ? を付加します。
interface RequestConfig {
url: string;
method?: string;
timeout?: number;
}
同じ構造を持つオブジェクト型を複数箇所で作成する場合は、interface を使用するのが一般的です。interface は継承が可能であり、既存の定義を拡張できます。
interface Point2D {
x: number;
y: number;
}
interface Point3D extends Point2D {
z: number;
}
const position: Point3D = { x: 10, y: 20, z: 30 };
type と interface の違いとしては、interface はオブジェクト構造の定義に特化しており、type は聯合型やプリミティブ型などあらゆる型のエイリアスとして機能します。
タプル型
配列の要素数と各要素の型が固定されている場合、タプル型を使用します。例えば、座標データのように順序と型が重要な場合に有効です。
const coordinates: [number, number] = [35.6895, 139.6917];
これにより、要素数の増減や型不一致を防ぐことができます。
型推論の仕組み
TypeScript は変数宣言時に値が代入されている場合、自動的に型を推論します。明示的な型注解は省略可能です。
let count = 0; // number 型として推論
let message = 'Hello'; // string 型として推論
関数の戻り値も同様に推論されます。開発効率を上げるため、推論が明確な箇所では型注解を省略する推奨されます。
型アサーション
開発者が TypeScript よりも型の情報を確信している場合、型アサーションを使用して型を明示できます。特に DOM 操作などで、汎用的な型から具体的な要素型へキャストする際に使用します。
const link = document.getElementById('main-link') as HTMLAnchorElement;
// または
const btn = <HTMLButtonElement>document.getElementById('submit-btn');
as 構文が一般的に推奨されます。これにより、要素固有のプロパティ(href など)にアクセス可能になります。
リテラル型と列挙型
変数の値が特定の文字列または数値に限定される場合、リテラル型を使用します。const 宣言された変数は、その値自体が型として扱われます。
const status = 'success'; // 型は 'success'
let mode: 'read' | 'write' = 'read';
複数の定数値をグループ化管理するには、enum(列挙型)が利用できます。
enum HttpMethod {
Get = 'GET',
Post = 'POST',
Put = 'PUT'
}
function sendRequest(method: HttpMethod) {
console.log(method);
}
sendRequest(HttpMethod.Get);
enum は実行時にオブジェクトとして残りますが、聯合リテラル型はコンパイル時に消去されるため、単純な値の限定であれば聯合型の方が軽量です。
any 型の注意点
any 型を指定すると、型チェックが無効になり、あらゆる操作が可能になります。これは TypeScript の型安全性を損なうため、避けるべきです。
let data: any = { value: 1 };
data.foo = 'bar'; // エラーにならない
data(); // エラーにならない
やむを得ず型を特定できない場合でも、unknown 型などの使用を検討し、可能な限り具体的な型を定義することが重要です。
typeof 演算子による型取得
既存の変数やプロパティの型を参照するには、typeof 演算子を型コンテキストで使用します。
const config = { timeout: 5000, retry: 3 };
function applyConfig(settings: typeof config) {
// settings は { timeout: number; retry: number } として扱われる
}
これにより、型定義の重複を防ぎ、元の変数の変更に合わせて型も自動的に追従させることができます。