前提
この記事は、初学者の私が、サバイバルTypeScriptで学んだこと+調べたことをまとめたものになります。
従って、経験則に基づいた発言ではないため、一部慣習とは違っていたり、認知にずれがあったりする可能性がありますので、ぜひコメントで指摘していただけると幸いです。
要約
- TSでは型の構造に基づいて型を区別する
- 構造的型付け ⇔ 名前的型付け
- 型の名前が違っていても、必要なプロパティやメソッドが一致していれば代入可能
-
意図しない互換性に注意する必要があるが、
private
メンバーやブランドで回避できる
構造的型付けとは?
名前でなく、型の構造に基づいて型を区別する方法。オブジェクト型の様な、複数のメソッドやプロパティを持つ型において、柔軟性の高い代入が可能となる。
TypeScriptのほかにもGoなどで採用されており、一般的に名前的型付けと大別される。
//構造が同じで名前が違う2つの型
Class Person1{
name() {}
}
Class Person2{
name() {}
}
const person1: Person1 = new Person1;
const person2: Person2 = person1;//構造が同じなので、エラーは発生しない
名前的型付けについて
- 型同士の区別を型の名前で行う
-
Java
やC#
、Swift
で採用されている。
//構造が同じでクラス名が違う2つの型
class Person1 {
void name() {}
}
class Person2 {
void name() {}
}
Person1 person1 = new Person1();
Person2 person2 = person1 //Error 別の型という扱い
構造的部分型
異なる型のインスタンスについて、必要なプロパティ、メソッドがそろっていれば代入できる。例えば、下のコードに示したような
name,talk()を持つインスタンス
⇐ name,age,talk(),walk()を持つインスタンス
の代入が可能となる ※逆は不可
要求するプロパティ以外のものは使われず、代入の際に無視される
class Person1{
name: string;
age: number;
constructor(name: string,age: number){
this.name = name;
this.age = age;
}
talk(){}
walk(){}
}
class Person2{
name: string;
constructor(name: string,age: number){
this.name = name;
}
talk() {}
}
const person1: Person1 = new Person1("Taro",15);
const person2: Person2 = person1; //可能
const personOne: Person1 = person2 //Error
//person2はPerson1クラスに要求されるプロパティやメソッドを持っていない
名前的型付けでは?
名前的型付けでは継承を使うことで、名前的部分型として、オブジェクト間の型関係を表現する
TypeScriptでも継承は使うが、親クラスのインターフェースを守るために使われ、メソッドの引数や返り値の変更はエラーになる。
class Person1 extends Person2
構造的型付けが抱える問題
概要
構造的型付けが抱える問題として挙げられるのが、型間の不要な互換性である。構造が同じであれば同じ型であると認識してしまうことで、本来全く関係のない型同士を同じものとしてしまう。
例
class Person{
move() {}
}
class Car{
move() {}
}
//全く関係のない車と人間を同じ型だと認識してしまう
const person: Person = new Person();
const car: Car = person;
解決策
1. privateメンバーの追加
private
修飾子を使用することで、型は同じでもクラス固有のものを作成できる。
private修飾子で宣言されたプロパティは、外から直接呼び出せないことに注意する
class Person{
private name:string
}
2. ブランド型の定義
各クラスに異なるブランド型のプロパティを追加することで、クラス間の区別が可能になる。
ブランド名を同じにすることで、クラスのグループ化が可能になる
class Person{
__brand: "person"
move(){}
}
class Car {
__brand: "car"
move(){}
}
まとめ
以上になります。型でガチガチに縛られているイメージのあるTypeScriptですが、必要に応じて柔軟性を持たせている部分は綺麗ですね。
当方初学者なため、認知のずれや、モダンな開発現場ではもう使わない表現などがありましたらコメントで指摘していただけると幸いです。😸
参考資料