型の互換性
TypeScriptは構造的型システム(structural typing)を採用しており、型の名前ではなく中身(構造)で互換性を判断します。
✅ 互換性のある例
type Point2D = { x: number; y: number };
type Point3D = { x: number; y: number; z: number };
const point3D: Point3D = { x: 0, y: 0, z: 0 };
const point2D: Point2D = point3D; // OK: point3Dはpoint2Dの構造を満たしている
❌ 互換性のない例(不足するプロパティ)
const point2D: Point2D = { x: 0, y: 0 };
const point3D: Point3D = point2D; // エラー: 'z' がない
🧠 テクニック集
1. as const でリテラル型を保つ
const direction = 'left' as const;
// typeof direction === 'left'
2. Readonly / readonly で不変性を担保
type User = {
readonly id: number;
name: string;
};
const u: User = { id: 1, name: "Alice" };
u.id = 2; // エラー: 読み取り専用
🛑 型互換性に注意すべきパターン
1. 関数のパラメータの逆変性(バリアント)
type Animal = { name: string };
type Dog = { name: string; bark: () => void };
let animal: (a: Animal) => void;
let dog: (d: Dog) => void;
animal = dog; // エラー(--strictFunctionTypes 有効時)
dog = animal; // OK
2. オプションと必須プロパティの互換性
type A = { x: number };
type B = { x?: number };
const a: A = { x: 1 };
const b: B = a; // OK
// const a2: A = b; // エラー: x がある保証がない