【TypeScript入門】クラスの型定義(インターフェースと型エイリアスの違い)

こんにちは、フロントエンドエンジニアのてりーです。

クラスの型定義について復習

前回に記事ではTypeScriptにおける、関数とクラスの基本的な型の定義方法と、クラスの継承・合成についてみていきました。

【TypeScript入門】クラスの型定義(インターフェースと型エイリアスの違い)

この記事の中で、クラスは型定義を目的とした抽象クラスと、実装部分の2通りに分かれると伝えました。


今回はこの部分を詳しく見ていきましょう。

インターフェース

抽象クラスには現状でabstract修飾子を使った方法と、インターフェースを使った方法があります。
abstract修飾子を使った方法は抽象クラスでありながら、実装部分を含む事ができる為、保守性を考えて現状ではあまり使われていません。

今回はインターフェースのみを説明していきます。

//インターフェースにてShapreを定義
interface Shape {
  readonly name: string;
  getArea: () => number;
}

//インターフェースにてQuadrangleを定義
interface Quadrangle {
  side: number;
}

//Rectabgleクラスには抽象クラスのShapreとQuadrangleの型を使う!!
class Rectangle implements Shape, Quadrangle {
  readonly name = 'rectangle';
  side: number;
  sideB: number;
  constructor(side: number, sideB: number) {
    this.side = side;
    this.sideB = sideB;
  }
  getArea = (): number => this.side * this.sideB;
}

const rect = new Rectangle(6, 5);
console.log(rect.getArea());

この様に

interface Quadrangle {
  side: number;
}

で定義できて、

class Rectangle implements Quadrangle { }

で使う事ができる!!詳しい使い方などはこちらの記事が参考になります。

Typescriptのinterfaceの使い方

型エイリアス

任意の型に別名を与えて再利用できるものを「型エイリアス」と呼びます。
定義の仕方はインターフェース同様です。

type Enemy = {
  hp: number
  mp: number
}

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

基本的に、現在ではインタフェースよりも型エイリアスの方ができる事が多いです。
その上で型エイリアスの方が柔軟性と保守性が高い為、モダンな環境では型エイリアスの方が好まれています。

それぞれの違いについてみていきましょう。

型エイリアスは任意の型を定義できる

type Unit = 'USD' | 'EUR' | 'JPY' | 'GBP';

などの様にリテラル型なども使えます。インターフェースではオブジェクトとクラスのみしか参照できません。

インターフェースは既存の方を名前を変えずに拡張できてしまいます。

interface User {
 age: number; 
}

interface User {
 species: 'rabbit' | 'bear' | 'fox' | 'dog';
}

型エイリアスはユニオン型とインターセクションで複雑な型表現ができる

 

ユニオン型

ユニオン型が『A または B』と適用範囲を増やしていく型の表現方法です。

type A = {
  foo: number;
  bar?: string;
};
type B = { foo: string };
type C = { bar: string };
type D = { baz: boolean };

typeAorB=A|B; //ユニオン型AまたはB{foo:number|string;bar?:string} 
typeAorC=A|C; //ユニオン型AまたはC{{foo:number;bar?:string}or{bar:string} 
typeAorD=A|D; //ユニオン型AまたはD{{foo:number;bar?:string}or{baz:boolean}

インターセクション型

インターセクション型は『A かつ B』と複数の型をひとつに結合させる事ができます。
→オブジェクト型の合成が可能になります。

type A = { foo: number };
type B = { bar: string };
type C = {
  foo?: number;
  baz: boolean;
};

typeAnB=A&B; //インターセクション型AかつB{foo:number,bar:string} 
typeAnC=A&C; //インターセクション型AかつC{foo:number,baz:boolean} 

ユニオン型とインターセクション型を組み合わせることで、複雑な型表現も可能です。

type A = { foo: number };
type B = { bar: string };
type C = {
  foo?: number;
  baz: boolean;
};

typeCnAorB=C&(A|B); 
// Cかつ(AまたはB)
//{ foo: number, baz: boolean } or { foo?: number, bar: string, baz: boolean }

型の Null 安全性を保証する

TypeScript はデフォルトの設定ではすべての型に null と undefined を代入できてしまいます。


このままだと静的型付け言語の特徴でもあるnull安全性を担保できません。

対策として、コンパイラオプション strictNullChecks を設定する必要があります。

りあクト! TypeScriptで始めるつらくないReact開発 第3版【Ⅰ. 言語・環境編】

tsconfig.json ファイルに対して次の様に記述してください。

"strictNullChecks": true,

もしnullを許容したい場合はこの様に書けばOKです。

let foo: string | null = 'fuu';

 

まとめ

今回はクラスの型定義の方法となる

・インターフェース
・型エイリアス

についてをメインに解説していきました。

現状では、型エイリアスの方が機能として優れていると説明しましたが、現場レベルではまだまだインターフェースが使われている場合もある為、今回はインターフェースの解説も混ぜました。

もし、新しくコードを書く際があれば、型エイリアスでの書き方をお勧めします。

また、今回の記事で、TypeScriptの基本的な型は全て解説しました。
ここまでの内容を抑えていれば、TypeScriptのコードを読む際にもわかる事が増えていると思います。

 

次回以降は応用編として、より高度な型表現を解説していきます。
以上です。

TypeScriptの入門記事一覧

【TypeScript入門】JavaScriptとの違いや特徴を詳しく解説
【TypeScript入門】基本的な型の定義方法
【TypeScript入門】関数とクラスの型定義(継承と合成)
【TypeScript入門】クラスの型定義(インターフェースと型エイリアスの違い)
【TypeScript】高度な型表現について
【TypeScript】型ガードと型アサーションでunknown型を使い勝手良くする