カテゴリー: FrontEnd

TypeScript初心者が押さえておくべき型システム

はじめに

今回の記事では、TypeScript の型システムについて、TypeScript を初めて触る人が押さえておくと良い、基礎的な内容を説明します。

型アノテーション

はじめに、TypeScript で扱える型について説明します。

プリミティブ型

JavaScript のプリミティブ型は、TypeScript では string 、number 、boolean として用意されています。

型アノテーションは、以下のように :TypeAnnotation 構文で書きます。

var val: number = 1;

リテラル型

ある変数を、特定の文字列のみを受け付けるようにしたい場合、文字列リテラルを型として使うことができます。

gender: 'male' | 'female';
var gender = 'male';

また、文字列の他に、number 型と boolean 型でも同様に型として使うことができます。

any

any 型はすべての型と互換性があります。そのため、 any 型の変数にはすべての型を代入することができ、他のどの型の変数にも any 型の変数を代入することができます。

var hoge: any;
hoge = 1;
hoge = '1';

nullとundefined

null と undefined は、any 型と同じように扱われます。この二つは他のどの型の変数にも代入することができます。( tsconfig.json の strictNullChecks が true の場合はエラーになります

var num: number;
var str: string:
num = null;
str = undefined;

ユニオン型

ある変数に複数の型を代入できるようにしたい場合、 | を使って書くことができます。

var hoge: string | number;

上の例では、 hoge には string 型、または number 型の値を代入することができます。また、オブジェクト型も同様に、union 型として定義することができます。

interface Hoge {
  a: number;
}
interface Fuga {
  b: number;
}
var hogeFuga: Hoge | Fuga;

上の例では、 hogeFuga は、 Hoge 、または Fuga を代入することができますが、それぞれが持っているパラメータが異なるため、後述する型ガードと合わせて使われることが多いでしょう。

交差型

交差型では、異なる複数のオブジェクトの機能を併せ持つ新しいオブジェクトを作成することができます。

interface Hoge {
  a: number;
}
interface Fuga {
  b: number;
}
var hogeFuga: Hoge & Fuga;
hogeFuga = {
  a: 1,
  b: 2
}

上の例では、 hogeFuga は Hoge が持っている a というプロパティと、 Fuga が持っている b というプロパティを持っています。

タプル型

タプルの実態は配列ですが、以下のようにアノテーションをつけることができます。

var hoge: [number, string];
hoge = [1, '1'];

列挙型

JavaScript には列挙型(enum)はありませんが、TypeScript では以下のように書くことができます。

enum Colors {
  Red,
  Green,
  Blue
}

これらの値は0からインクリメントされる数値です。以下のように、始めの値を指定することができます。

enum Colors {
  Red = 5,
  Green,
  Blue
}

また、値を文字列にすることもできます。

enum Colors {
  Red = 'red',
  Green = 'green',
  Blue = 'blue'
}

ほかにも、静的関数をもたせることなどもできます。より詳しく知りたい方はこちらをご覧ください。

配列

配列の宣言は、有効な型アノテーションの後ろに [] をつけます。

var numbers: number[];

interface

interface は、複数の型アノテーションを一つのアノテーションに合成するために使われます。

interface Profile {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

なお、あるクラスに interface を準拠させたい場合、 implementes キーワードを使うことができます。

interface Profile { 
  name: string;
  age: number;
  gender: 'male' | 'female'; 
}

class CProfile implements Profile {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

インライン型アノテーション

1度だけ使われるアノテーションの場合、インライン型で定義することで型名を考える手間を省くことができます。

var profile: {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

型エイリアス

以下のように、型アノテーションに名前をつけることができます。

type Gender = 'male' | 'female';
var gender: Gender;

generics

generics な関数は以下のように定義します。

function reverse<T> (items: T[]): T[] {
  var tmp = [];
  for (let i = items.length - 1; i >= 0; i++) {
    tmp.push(items[i]);
  }
  return tmp;
}

var numbers = [1, 2, 3];
var rNumbers = reverse(numbers);

generics についてもっと詳しく知りたい方は、こちらをご覧ください。

関数の型

関数のパラメータにつけることができる型アノテーションについて説明します。

引数と戻り値の型アノテーション

関数には、引数と戻り値に型アノテーションをつけることができます。

// 関数の型アノテーション(戻り値は number型)
function square(val: number): number {
  return val * val;
}

また、関数が値を返す必要がない場合、:void 型アノテーションをつけることができます。

function alert(val: string): void {
  alert(val);
}

なお、コンパイラが型推論できる場合、戻り値のアノテーションを省略することができます。

オプションパラメータ

以下のように省略可能なパラメータを定義することができます。

function hoge(a: number, b?: number) {}
hoge(1);
hoge(1, 2);

// デフォルト値を設定することもできる
function huga(a: number, b: number = 2) {}
huga(1);
huga(1, 2);

overload

以下のように関数の overload を宣言することができます。

function hoge(abcd: number);
function hoge(ac: number, bd: number);
function hoge(a: number, b: number, c: number, d: number);
function hoge(a: number, b?: number, c?: number, d?: number) {
  if (!b && !c && !d) {
    b = c = d = a;
  } else if (!c && !d) {
    c = a;
    d = b;
  }
  return {
    a: a,
    b: b,
    c: c,
    d: d
  };
}

上の例では、関数 hoge は引数を1つ、または2つ、または4つ受け付けます。引数を3つ渡した場合はエラーになります。

型推論

変数に型アノテーションを書かなくても、いくつかのルールに基づいて自動的に型を推論します。

変数の定義

変数宣言時の初期値によって型を推論します。

let hoge = 1; // number型
let fuga = '1'; // string型
hoge = fuga; // error

戻り値

return で返却する値によって型を推論します。

function hogeFunction() {
  return 'hoge';
}
var hoge = hogeFunction(); // number型
hoge = '1';
hoge = 1; // error

代入

代入される値によって型を推論します。

let hoge = 1;
let fuga = hoge; // number型
fuga = 1;
fuga = '1' // error

構造化

構造化も同様に値によって型を推論します。

let hoge = { a: 1, b: '1' } // let hoge: { a: number, b: string } = { a: 1, b: '1' } と同じ
hoge.a = 2;
hoge.a = '2'; // error

分解

分解も同様に値によって型を推論します。

let hoge = { a: 1, b: '1' }
let { a } = hoge; // number型
a = 2;
a = '2'; // error

型アサーション

型アサーションは、TypeScript が推論した型を任意の型で上書きすることです。一般的には、JavaScript で書かれたコードを TypeScript へ移植する場合に用いられることが多いです。

interface Hoge {
  a: number;
  b: string;
}

var hoge = {} as Hoge;
hoge.a = 1;
hoge.b = '1';

注意点としては、必要なパラメータを設定し忘れても、コンパイラはエラーを表示しないため、予期せぬエラーを引き起こすことがあることです。可能であれば以下のようにするのが良いでしょう。

var hoge: Hoge = { a: 1, b: '1' };

型アサーションの成否

型アサーションは、型T が 型U のサブタイプであるか、その逆である場合に成功します。

interface Hoge {
  a: number;
  b: string;
}

var hoge1: Hoge = { a: 1 } as Hoge;
hoge1.a = 2;
hoge1.b = '2';
var hoge2: Hoge = { a: 1, b: '1', c: true } as Hoge;
hoge2.a = 2;
hoge2.b = '2';
hoge2.c = false; // error

型ガード

Type Guard を使用すると、ブロック内のオブジェクトの型を制限することができます。

typeof

var hoge: number | string;
if (typeof hoge === 'number') {
  hoge += 1;
  hoge.substr(1); // error
} else if (typeof hoge === 'string') {
  hoge.substr(1);
}

instanceof

クラスの場合は instanceof を使います。

class Hoge { a: number; }
class Fuga { b: number; }
var hoge: Hoge;
if (hoge instanceof Hoge) {
  hoge.a += 1;
  hoge.b += 1; // error
}

in

in 演算子はオブジェクトのプロパティをチェックします。これも TypeGuard として使用することができます。

interface Hoge { a: number; }
interface Fuga { b: number; }
var hoge: Hoge;
if ('a' in hoge) {
  hoge.a += 1;
}

さいごに

TypeScript 初学者向けに TypeScript の型システムについて紹介しました。まだ、 TypeScript を使っていない方は、ぜひ、 TypeScript を初めてみてください。

おすすめ書籍

   

Hiroki Ono

シェア
執筆者:
Hiroki Ono
タグ: TypeScript

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前