TypeScriptにおけるInterfaceの活用方法について確認します。TypeScriptのInterfaceは、オブジェクト指向としての利用だけでなく、型注釈としても活用することができます。
目次
Interfaceの利用用途
他の言語で Interface
と聞くと、implements
で実装して利用するものというイメージがありますが、TypeScriptでは 型注釈
としても利用されます。
- 型注釈としての利用
implements
を必要としない- TypeScriptは、Structural Subtyping(構造的部分型)という考え方を採用
- 型の構造と互換性があるかをチェック
- インタフェースが要求するプロパティが存在し、要求された型を持つことだけをチェック
- オブジェクト指向としての利用
implements
による実装を強制- Interfaceでは、定義だけ記述
- extendsによる多重継承はできないが、複数のインタフェースを実装することは可能
型注釈
型注釈 > 関数の引数
interface Xxx {
a: string;
b: number;
c: string[];
}
function echo(x: Xxx) {
console.log(x.a);
console.log(x.b);
console.log(x.c);
}
// OK
const obj1 = {a: 'aaa', b: 11, c: ['a', 'b']};
echo(obj1);
// OK
// 順序は関係ない
const obj2 = {c: ['a', 'b'], a: 'aaa', b: 11};
echo(obj2);
// OK
// 余計なプロパティが存在していても良い
const obj3 = {a: 'aaa', b: 11, c: ['a', 'b'], d: 'hhh'};
echo(obj3);
// NG
// 直接引数指定を行うとき、余計なプロパティが存在する場合はNG
// Object literal may only specify known properties, and 'd' does not exist in type 'Xxx'.
// echo({a: 'aaa', b: 11, c: ['a', 'b'], d: 'hhh'});
// NG
// プロパティが不足している
// const obj4 = {a: 'aaa', b: 11};
// echo(obj4);
// NG
// プロパティの型が異なる
// const obj5 = {a: 11, b: 11, c: ['a', 'b']};
// echo(obj5);
型注釈 > 関数型(Function)
interface TestFunc {
(aaa: number, bbb: string): boolean;
}
let funcA: TestFunc;
funcA = (aaa: number, bbb: string): boolean => {
console.log(bbb);
return aaa > 0
};
// パラメーター名が異なっていてもOK
let funcB: TestFunc;
funcB = (ccc: number, ddd: string): boolean => {
console.log(ddd);
return ccc > 0
};
// パラメーターが不足していてもOK
let funcC: TestFunc;
funcC = (): boolean => {
return true
};
// 1番目のパラメーターの型が異なるのでNG
// let funcD: TestFunc;
// funcD = (bbb: string, aaa: string): boolean => {
// return true
// };
// 戻り値の型が異なるのでNG
// let funcE: TestFunc;
// funcE = (aaa: number, bbb: string) => {
// return 1
// };
プロパティ修飾子
( ?|省略可能, readonly|読込専用 )
interface Yyy {
a?: string;
b?: string;
readonly c: string;
}
function echo(y: Yyy) {
let parameter = {a: 'aaa', b: 'bbb', c: ''};
if (y.a) {
parameter.a = y.a
}
if (y.b) {
parameter.b = y.b
}
parameter.c = y.c;
// readonlyのためNG
// Cannot assign to 'c' because it is a constant or a read-only property.
// y.c = 'ccc';
console.log(parameter);
}
// OK
echo({a: 'AAA', b: 'BBB', c: 'ccc'}); // { a: 'AAA', b: 'BBB', c: 'ccc' }
// OK
// a, bは省略可能項目
echo({c: 'ccc'}); // { a: 'aaa', b: 'bbb', c: 'ccc' }
Index Signature
interface Zzz {
name: string;
[key: string]: string;
}
function echo(z: Zzz) {
console.log(z);
}
// OK
echo({ name: 'x' });
echo({ name: 'x', a: 'y' });
echo({ name: 'x', a: 'y', b: 'z' });
// Error: Type 'number' is not assignable to type 'string'.
echo({ name: 'x', a: 111 });
[key: string]: string;
のように、任意キーを定義できます。
オブジェクト指向としての利用
implements|インタフェースを実装
interface
でクラスのパブリックなメンバを定義します。implements
で定義した interface
を実装します。
interface InterfaceA {
sampleProperty: string
sampleFunction(x: number): boolean
}
class ClassA implements InterfaceA {
sampleProperty: string = 'hello';
sampleFunction(x: number): boolean {
return x > 0
}
}
const classA = new ClassA();
console.log(classA.sampleProperty); // hello
console.log(classA.sampleFunction(100)); // true
extends|インタフェースを継承
interface
は extends
で継承することができます。
interface InterfaceA {
sampleProperty: string
}
interface InterfaceB extends InterfaceA {
sampleFunction(x: number): boolean
}
class ClassB implements InterfaceB {
sampleProperty: string = 'hello';
sampleFunction(x: number): boolean {
return x > 0
}
}
const classB = new ClassB();
console.log(classB.sampleProperty); // hello
console.log(classB.sampleFunction(100)); // true
複数インタフェースを実装
複数のインタフェースを指定して、実装できます。
interface InterfaceA {
sampleProperty: string
}
interface InterfaceB {
sampleFunction(x: number): boolean
}
class MyClass implements InterfaceA, InterfaceB {
sampleProperty: string = 'hello';
sampleFunction(x: number): boolean {
return x > 0
}
}
const myClass = new MyClass();
console.log(myClass.sampleProperty); // hello
console.log(myClass.sampleFunction(100)); // true