サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

TypeScriptハンドブック 04: 型エイリアスとインタフェース


オブジェクトの型は、型注釈で定められました(「TypeScriptハンドブック 02: 変数と関数およびオブジェクトの型」参照)。けれど、型を複数のモジュールから使いたいとき、名前(識別子)をつけて定義した方が便利でしょう。そのときに用いるのが、型エイリアスとインタフェースです(以下のリストの英語タイトルは公式「Handbook」へのリンク)。

01 型エイリアス

変数や関数あるいはオブジェクトの型は、型注釈で決められました(「TypeScriptハンドブック 02: 変数と関数およびオブジェクトの型」参照)。でも、同じ型をほかでも使うとき、いちいち注釈を書き加えるのは煩わしいですし、間違いのもとにもなりかねません。その型注釈に名前をつけて、使い回せるようにしようというのが型エイリアスです。型エイリアスはtypeキーワードで定めます。

type 型エイリアス = 型定義

つぎの関数(getLenth())は、引数を型エイリアス(Point)で型づけました。なお、引数からのプロパティの取り出しには、オブジェクトの分割代入を用いています。


type Point = {
	x: number;
	y: number;
};
function getLenth({ x, y }: Point) {
	return Math.hypot(x, y);
}
console.log(getLenth({ x: 3, y: 4 }));  // 5

型エイリアスには、どのような型を与えることも可能です。つぎの例では、ユニオン型にタイプエイリアスの名前をつけました。


type ID = number | string;

エイリアス(alias)は「別名」という意味です。つまり、型に名前をつけるだけで、新しい型をつくるわけではありません。構文が代入式と同じ代入演算子=で定められているのも、そのことを示します。したがって、つぎのコード例は、エラーも警告も表れません。ふたつのエイリアス(PointVector)の右辺に書かれた型注釈は同じだからです。


type Point = {
	x: number;
	y: number;
};
type Vector = {
	x: number;
	y: number;
};
let point: Point = {x: 3, y: 4};
const vector: Vector = {x: 1, y: Math.sqrt(3)};  // エラーなし

02 インタフェース

インタフェース宣言は、オブジェクトの型を定義します。宣言ですので、代入演算子=は用いません。プロパティの型の定め型は、型エイリアスと同じです。型づけの仕方も、型エイリアスとまったく変わりません。


interface Point {
	x: number;
	y: number;
}
function getLenth({ x, y }: Point) {
	return Math.hypot(x, y);
}
console.log(getLenth({ x: 3, y: 4 }));  // 5

上のコード例で、関数(getLenth())に渡したのはオブジェクトリテラルでした。つまり、インタフェース(Point)では型づけされていません。それでもエラーにならないのは、決められた型にデータをあてはめられるかどうかは、TypeScriptが型の構造で判断しているからです。つまり、同じプロパティを同じ型で備えていれば、型の互換性があるとみなされます(「TypeScript: 異なる2つの型システム『公称型』と『構造的部分型』」参照)。

型エイリアスとインタフェースの違い

かつては、インタフェースにできても、型エイリアスではできないことが多くありました。けれど、アップデートにともない、その差はほぼなくなったといえるでしょう。ここでは、残されたいくつかの違いをご説明します。まず、インタフェースは拡張(extends)できます。


interface Point {
	x: number;
	y: number;
}
interface Point3D extends Point {
	z: number;
}
function getLenth({ x, y, z }: Point3D) {
	return Math.hypot(x, y, z);
}
console.log(getLenth({ x: 3, y: 4, z: 5 * Math.sqrt(3) }));  // 10

もっとも、型エイリアスでも交差型(intersection type)を用いれば同じ結果が得られます。


type Point = {
	x: number;
	y: number;
};
type Point3D = Point & {
	z: number;
};

インタフェースにしかできないのは、同じ名前のinterface宣言によりフィールドを加えることです。


interface Point {
	x: number;
	y: number;
}
interface Point {
	z?: number;
}
function getLenth({ x, y, z = 0 }: Point) {
	return Math.hypot(x, y, z);
}
console.log(getLenth({ x: 3, y: 4 }));  // 5
console.log(getLenth({ x: 3, y: 4, z: 5 * Math.sqrt(3) }));  // 10

型エイリアスは、変数宣言と同じく、同じ名前が重複することを許しません。


type Point = {
	x: number;
	y: number;
};
// 識別子 'Point' が重複しています。
type Point = {
	z?: number;
};

型エイリアスでないとできないのは、プリミティブに別の名前を与えることです。インタフェースが定めるのはオブジェクトの型ですから、構文上このようなことはできません。


type StringValue = string;
const name: StringValue = 'Alice';


作成者: 野中文雄
作成日: 2021年05月11日


Copyright © 2001-2020 Fumio Nonaka.  All rights reserved.