項目4 構造的型付けに慣れる
JavaScriptはダックタイピングを推奨している
TypeScriptは、構造的型システムを使って、ダックタイピングの動作をモデリングする
型が違えど、構造に互換性があれば、型エラーにはならない
code:ts
interface Vector2D {x: number; y: number;}
interface NamedVector {name: string; x: number; y: number;}
function calculateLength(v: Vector2D) {
return Math.sqrt(v.x ** 2 + v.y ** 2);
}
const v: NamedVector = { x: 3, y: 4, name: 'Pythagoras' };
// エラーにならない
calculateLength(v);
構造的型システムがトラブルを起こすケースもある
値が型宣言に明示したプロパティ以外のプロパティを持つことによって生まれる不具合
関数を書くときは、宣言したプロパティを持ち、それ以外は持たない引数で呼び出されることを想定しがち。これを閉じた型、シールされた型という
Vector3Dの型定義上ではaxisはnumberだが、他のプロパティを持つ可能性があるためstring型と推論される
code:ts
type Vector3D = {x: number; y: number; z: number;}
function calculateLengthL1(v: Vector3D) {
let length = 0;
for (const axis of Object.keys(v)) {
// axisがnumber型とは限らないのでcoordはanyとみなされる
const coord = vaxis;
///
}
return length;
}
オブジェクトの反復処理を正しく型付けするのは難しい
詳細は項目60 オブジェクトに対して反復処理する方法を知る
クラスでも構造的型システムが扱われる
クラスの構造とオブジェクトの構造が一致しているため、代入可能
code:ts
class SmallNumContainer {
num: number;
constructor(num: number) {
// numが0未満または10以上の場合はエラーを投げる
if (num < 0 || num >= 10) {
throw new Error(You gave me ${num} but I want something 0-9.)
}
this.num = num;
}
}
const a = new SmallNumContainer(5);
const b: SmallNumContainer = { num: 2025 }; // 問題ない
構造的型システムはモックやライブラリ間の依存関係を剥がすのに活用できる
詳細は項目70 型を部分的にコピーして依存を断ち切る
#TypeScript