HTML5テクニカルノート
TypeScriptハンドブック 04: 型エイリアスとインタフェース
- ID: FN2104004
- Technique: ECMAScript 2015
- Package: TypeScript 4.2
オブジェクトの型は、型注釈で定められました(「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)は「別名」という意味です。つまり、型に名前をつけるだけで、新しい型をつくるわけではありません。構文が代入式と同じ代入演算子=
で定められているのも、そのことを示します。したがって、つぎのコード例は、エラーも警告も表れません。ふたつのエイリアス(Point
とVector
)の右辺に書かれた型注釈は同じだからです。
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.