この記事では、TypeScriptにおけるIntersection Type (交差型) の使い方と、配列要素の型拡張と配列オブジェクト自体の型拡張との違いについて解説します。
前提となる型定義
type UserType = {
id: number;
name: string;
};
型の拡張:Intersection Type
Intersection Typeは、既存の型に別の型を合成し、新しい型定義を構築するTypeScriptの機能です。定義の仕方は以下の通りです。
型エイリアスを定義する(新しい型名を与える)
型定義として新しい名前(エイリアス)を与え、再利用可能な拡張型を作成します。
type UserWithArea = UserType & {
isKanto?: boolean;
isTohoku?: boolean;
};
const users: UserWithArea;
一時的な拡張(新しい型名を与えない)
新しい型エイリアスを定義せず、その場限りで型を拡張します。これは変数の型注釈などで利用されます。
const users: (UserType & { isKanto?: boolean; });
Intersection Typeの挙動と特性
拡張された型で宣言された変数では、合成されたプロパティ(例: isKanto)が参照可能になります。
const user: UserWithArea = fetchUser;
user.isKanto = true; // 型エラーにならず、参照・代入が可能
この定義は、元のUserTypeに一切影響を与えません。UserTypeは引き続きidとnameのみを持つ型として扱われます。純粋に型情報の合成であり、JavaScriptの実行時のオブジェクトには影響しません。
誤解を生む例:配列オブジェクト自体の拡張
Intersection Typeを、配列の要素の型ではなく、配列オブジェクト自体の型に適用すると、意図しない挙動になります。
users: UserType[] & {
isKanto?: boolean;
isTohoku?: boolean;
};
この型で宣言された変数では、配列オブジェクト自身にisKantoプロパティが参照可能になります。
const users: UserType[] & { isKanto?: boolean; } = [
{ id: 1, name: "A" }
];
users.isKanto = true; // OK: 配列オブジェクト自体へのプロパティ参照が許可される
users[0].isKanto = true; // NG: 要素 (users[0]) の型は UserType のままであるため
配列の要素一つひとつの型を拡張したい場合は、必ず括弧 () を使用して要素の型に対して Intersection Type を適用します。
users: (UserType & { isKanto?: boolean; })[];
まとめ
型の拡張(Intersection Type)はコンパイル時の型定義操作であり、既存の型に影響を与えずにプロパティの参照を許可します。特に配列を扱う場合、配列オブジェクトの拡張)と配列要素の拡張の違いを明確にすることが重要です。