TypeScriptでinterfaceを使う
TypeScriptでinterfaceを使う
interfaceはオブジェクトとClassにのみ型の指定ができます。interfaceに似たものにtypeエイリアスがありますが、typeエイリアスはすべてに型指定ができます。
使い方は以下。
interface Students {
name: string;
age: number;
}
const students: Students = {
name: '山田',
age: 30,
};
interfaceでメソッドの型を指定する
interface Students {
name: string;
age: number;
//hello: () => void; こっちの書き方でもOK
hello(): void;
}
const students: Students = {
name: '山田',
age: 30,
hello() {
console.log(`私の名前は${students.name}です。年齢は${students.age}歳です`);
},
};
students.hello();
4行目と5行目にある通り、メソッドの型を定義するときは2種類の書き方があることを覚えておきましょう。
Classに対してinterfaceの型を適用する
Classに対してinterfaceで定義した型を指定するためには、implementsを使用します。
interface Students {
name: string;
age: number;
//hello: () => void; こっちの書き方でもOK
hello(): void;
}
class studensGlup implements Students {
constructor(public name: string, public age: number, public height:number) {}
hello() {
console.log(`私の名前は${this.name}です。年齢は${this.age}歳です`);
}
}
interfaceに定義した型は少なくともClass内で満たす必要があります。つまり、nameやage、helloメソッドのほかに何かプロパティを追加することは可能です。(例としてheightを追加してみました)
ちなみにinterfaceではなく、typeエイリアスでもimpementsを使ってもClassに型を指定できます。
1つのClassに対して、複数のinterfaceを適用する
implementsを使うことで複数のinterfaceをClassに適用させることができます。
interface Students {
name: string;
age: number;
hello(): void;
}
interface Adress {
postNumber: number;
adress: string;
}
class StudensGlup implements Students, Adress {
constructor(public name: string, public age: number, public postNumber: number, public adress: string) {}
hello() {
console.log(`私の名前は${this.name}です。年齢は${this.age}歳です`);
}
}
ClassのstudensGlupにinterfaceで定義したStudentsとAdressの2つを定義したので、constructorにはさらにpostNumberとadressを追加する必要があります。
かならずinterfaceに定義した型がすべて必要なのか?
例えば以下の形だと当然interfaceに定義されていない型があるのでエラーになります。
/*-- エラーになる例 --*/
interface Students {
name: string;
age: number;
hello(): void;
}
const students: Students = {
name: '山田',
age: 30,
specializedField: 'Math', //interface Studentsに定義されていないのでエラー
hello() {
console.log(`私の名前は${students.name}です。年齢は${students.age}歳です`);
},
};
ただ以下のように、一度変数と通すことでエラーは表示されなくなります。
interface Students {
name: string;
age: number;
hello(): void;
}
const students = {
name: '山田',
age: 30,
specializedField: 'Math',
hello() {
console.log(`私の名前は${students.name}です。年齢は${students.age}歳です`);
},
};
const scholl: Students = students; //エラーは表示されない
変数scollは最低でもinterfaceに定義されたStudentsの条件さえ満たせていれば問題ないということ。もちろん、scholl.specializedFieldではアクセスできません。(Studentsの型しか持っていないため)
interfaceのreadonly修飾子を指定して、後から書き込めないようにする
オブジェクトは定義した後でも、プロパティの値を変更することができます。
interface Students {
name: string;
age: number;
hello(): void;
}
const students: Students = {
name: '山田',
age: 30,
hello() {
console.log(`私の名前は${students.name}です。年齢は${students.age}歳です`);
},
};
students.name = '鈴木'; //値を変更することができる。
後から変更させたくない場合は、interfaceの型にreadonly修飾子をつけます。
interface Students {
readonly name: string;
readonly age: number;
hello(): void;
}
const students: Students = {
name: '山田',
age: 30,
hello() {
console.log(`私の名前は${students.name}です。年齢は${students.age}歳です`);
},
};
students.name = '鈴木'; //エラーになる
students.age = 20; //エラーになる
Classにreadonly修飾子があるinterfaceを指定した場合は?
Classの場合は、interfaceにreadonly修飾子があっても無視されます。
interface Students {
readonly name: string;
readonly age: number;
hello(): void;
}
class StudensGlup implements Students {
constructor(public name: string, public age: number, public height: number) {}
hello() {
console.log(`私の名前は${this.name}です。年齢は${this.age}歳です`);
}
}
const studentsGlup = new StudensGlup('山田', 30, 3220024);
studentsGlup.name = '鈴木'; //ここで変更可能
8行目のcontructorで、nameやageのプロパティを初期化するときにpublicを指定しているため、こちらが優先されて適用されます。その結果、15行目でnameプロパティが変更できています。
もちろん、constructorでnameやageに対してreadonlyを指定すれば外部から値を変更することはできなくなります。
interfaceをextendsで継承する
interfaceを他のinterfaceに継承させる場合はextendsを使います。
interface Adress {
postNumber: number;
adress: string;
}
interface Students extends Adress {
name: string;
age: number;
hello(): void;
}
const students: Students = {
postNumber: 3330015,
adress: '東京',
name: '山田',
age: 30,
hello() {
console.log(`私の名前は${students.name}です。年齢は${students.age}歳です`);
},
};
Studentsのinterfaceに、Adressのinterfaceをextendsで継承しています。interfaceのStudensを指定された変数studentsは、あらたにpostNumberとadressを追加する必要があります。(12、13行目)
interfaceを複数継承できるのか
interfaceは複数継承できます。
interface SpecializedField {
specializedField: string;
}
interface Adress {
postNumber: number;
adress: string;
}
interface Students extends Adress, SpecializedField {
name: string;
age: number;
hello(): void;
}
typeエイリアスをinterfaceに継承できるのか
typeエイリアスからinterfaceに継承できます。
type SpecializedField = {
specializedField: string;
};
type Adress = {
postNumber: number;
adress: string;
};
interface Students extends Adress, SpecializedField {
name: string;
age: number;
hello(): void;
}
継承元が同じプロパティを持っているとき、上書きされるのか
条件によっては上書きされます。
interface Adress {
name: string;
postNumber: number;
adress: string;
}
interface Students extends Adress{
name: any;
age: number;
//hello(): void;
hello: () => void;
}
interfaceのStudentsにあるnameにはanyがあり、interfaceのAdressにあるnameにはstringがあります。anyには何でも入るため、nameはany型として上書きされます。
interface Adress {
name: number;
postNumber: number;
adress: string;
}
interface Students extends Adress { //nameのstringに、Adressのnameにあるnumberが割り当てられないのでエラー
name: string;
age: number;
hello(): void;
}
interfaceのStudentsにあるnameにstringを指定して、interfaceのAdressにあるnameにはnumberがある場合は、stringにnumberを割り当てることができないため、エラーになります。
interfaceで関数の型を指定する
関数の型についてinterfaceで指定することもできます。
/* typeエイリアスの場合はこちら
type NumberAdd = (n1: number, n2: number) => number;
*/
interface NumberAdd {
(n1: number, n2: number): number;
}
const numberAdd: NumberAdd = (num1: number, num2: number) => {
return num1 + num2;
};
numberAdd(11, 20);
あっても無くても良いプロパティやメソッドは「?」で指定できる
以下のようにnameプロパティやhelloメソッドに対して「?」を指定することで、あっても無くても良い状態にできます。
interface Students {
name?: string; //もしnameがあるなら…
age: number;
hello?(): void; //もしhelloメソッドがあるなら…
}
const students: Students = {
//nameプロパティやhelloメソッドがなくてもOK
age: 30,
};
また、関数が受け取るパラメーターに対しても「?」は使えます。注意点は、「?」がついたパラメーターは「?」がついていないパラメーターより後に書かないとエラーになります。
const numberAdd = (num1?: number, num2?: number) => {
if (num1 && num2) {
return num1 + num2;
}
};
具体的にどのように型推論されているか見てみると以下です。

num1やnum2にはundefinedも設定されていて、返す型にもundefinedが設定されているのがわかります。実際にパラメーターに「?」を使う場合はif文で条件分岐して使うようにしましょう。
関数のパラメーターに初期値を設定する
「?」のようにあるか無いかではなく、パラメーターに初期値を設定することもできます。これはTypeScriptの機能ではなく、JavaScriptに備わっている機能です。
const numberAdd = (num1 = 1, num2 = 3) => { //初期値としてnum1には1を、num2には3を代入している
return num1 + num2;
};
console.log(numberAdd()); //4と表示。 引数に何も設定していないと、num1とnum2にある初期値が入る
console.log(numberAdd(90, 200));//290と表示。 引数があればその値が適用される
パラメーターに初期値を設定した場合は、パラメーターに「?」や型の指定があるとエラーになるので注意しましょう。