Writer: tokuyasu 更新日:2024/05/22
こんにちは、デジナーレ福岡オフィスの徳安です。
今回はTypeScriptのtype と interfaceについてまとめてみました。
interface(インターフェース)
interfaceは、クラスやオブジェクトが持つ、
プロパティやメソッドを定義した型のことを指す。
1 2 3 4 5 6 7 8 9 |
interface Person { name: string; age: number; } const person: Person = { name: "John", age: 5, }; |
type(型エイリアス)
typeは、型に名前を付けることができる。
名前のついた型を、型エイリアス(タイプエイリアス: type alias)と呼ぶ。
1 2 3 4 5 6 7 8 9 |
type Person = { name: string; age: number; }; const person: Person = { name: "John", age: 5, }; |
1 2 |
// NOTE: string | number型に型名を付ける type StringOrNumber = string | number; |
※interface では、上記のような型定義はできない。
typeでの型定義では、ほかにも様々な型に名前を付けることができる。
1 2 3 4 5 6 7 8 9 10 11 12 |
// NOTE: プリミティブ型 type Str = string; // NOTE: リテラル型 type Literal = 200; // NOTE: 配列型 type Numbers = number[]; // NOTE: オブジェクト型 type UserObj = { id: number; name: string }; // NOTE: ユニオン型 type NumberOrNull = number | null; // NOTE: 関数型 type CallbackFunction = (value: string) => boolean; |
interfaceとtypeの違い
継承について
interface は、interfaceやtypeを継承することができる。
1 2 3 4 5 6 7 8 9 10 11 |
interface Animal { name: string; } type Creature = { dna: string; }; // NOTE: interfaceを使用すると、interface、typeのどちらも継承ができる interface Dog extends Animal, Creature { dogType: string; } |
typeでの型定義では、extendを使用して継承することはできない。
交差型を使用することで、extendでの継承と似たものを再現することは可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
type Animal = { name: string; }; type Creature = { dna: string; }; /* NOTE: typeではextendでの継承はできないが、 交差型を使用して、再現することは可能 */ type Dog = Animal & Creature & { dogType: string; }; |
継承による上書き
interfaceで継承の際に、プロパティを上書き(オーバーライド)すると、
継承元のプロパティの型が上書きされる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
interface Animal { // NOTE: Animalのnameの型はany name: any; price: { yen: number; }; legCount: number; // Dog } interface Dog extends Animal { // NOTE: Dogのnameの型はstring name: string; // Dog price: { yen: number; // Dog dollar: number; // Dog }; } // NOTE: 最終的なDogの定義 interface Dog { name: string; price: { yen: number; dollar: number; }; legCount: number; } |
※上記では、nameの型定義をany → string に上書きされているが、
上書きできるものは、元の型に代入できるもののみ。
下記のように、number型であるnumberFieldを、string型で上書きすることはできない。
1 2 3 4 5 6 7 |
interface A { numberField: number; } interface B extends A { numberField: string; // インターフェイス 'B' はインターフェイス 'A' を正しく拡張していません。 } |
typeでの型定義では、上書きにならずにフィールドの型の交差型が計算される。
交差型で計算ができない場合でもコンパイルエラーにはならない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type Animal = { name: number; price: { yen: number; dollar: number; }; }; type Dog = Animal & { name: string; price: { yen: number; euro: number; }; }; |
1 2 3 4 5 6 7 8 9 |
// NOTE: 最終的なDogの定義 type Dog = { name: never; // 交差型を作れない場合はコンパイルエラーではなくnever型になる price: { yen: number; dollar: number; euro: number; }; }; |
同名のものを宣言
interface の場合は、同じ名前のinterface を定義することができる。
定義したinterfaceは、すべての宣言を合成させたものになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
interface Animal { name: string; legCount: number; } interface Animal { price: { yen: number; }; } const animal: Animal = { name: "", legCount: 0, price: { yen: 1, }, }; |
同じ名前で型定義できるということは、気づかぬうちに同じ名前で宣言し、
定義した型の中身が変わってしまうことにもなりうるので、使う際には注意する必要がある。
typeでの型定義では、同じ名前での型定義はできない。
1 2 3 4 5 6 7 8 9 10 |
type Animal = { // 識別子 'Animal' が重複しています。 name: string; legCount: number; }; type Animal = { // 識別子 'Animal' が重複しています。 price: { yen: number; }; }; |
Mapped Types
Mapped Typesは型のキーを動的に指定することができる仕組み。
オブジェクトのキーに型定義をすることが可能。
typeでの型定義のみで実装することができる。
1 2 3 4 |
type Fruits = "apple" | "banana" | "strawberry" | "orange"; type FruitsObj = { [key in Fruits]: string; }; |
interfaceで使用するとエラーが発生する。
1 2 3 4 |
type Fruits = "apple" | "banana" | "strawberry" | "orange"; interface FruitsObj { [key in Fruits]: string; // マップされた型では、プロパティまたはメソッドを宣言しない場合があります。 }; |
今回はinterfaceとtypeでの型定義の違いについてまとめてみました。
interfaceとtypeのどちらを使用して型定義を行うかは、
それぞれのメリットやデメリットを理解して、
プロジェクト内でルールを決め、そのルールに則って開発をするようにします。
ここまで読み進めていただきありがとうございます。