TypeScriptの型づけを使う ー Union型、列挙型、型の別名など

TypeScriptには、JavaScriptに備わるデータ型だけでなく、ECMAScript 2015の仕様も採り入れて、型や機能が加えられています。それらを試してみましょう。なお、TypeScriptの型づけの基本については、「[TypeScript: 基本型](http://fumiononaka.com/Business/html5/FN1701009.html)」をお読みください。

# 01 カラー整数値からRGBカラー成分値を配列で返す関数
以下のコード001(TypeScriptサイトのPlaygroundにリンクしています)は、引数に渡したRGBカラーの整数値からRGBごとの成分値を配列で返す関数です。関数は、つぎのように試すことができます。なお、カラー整数値からRGBカラー成分値を求めるビット演算については、「2進数・16進数とビット演算」05「[カラー値とビット演算](http://www.fumiononaka.com/Business/html5/FN1509001.html#sec05)」をお読みください。

```ts
let components: number[] = getColorComponents(0xFFCC99);
console.log(components);  //  [ 255, 204, 153 ]
console.log(components[0].toString(16), components[1].toString(16), components[2].toString(16));
// ff cc 99
```

### [コード001](https://www.typescriptlang.org/play/index.html#src=let%20components%3A%20number%5B%5D%20%3D%20getColorComponents(0xFFCC99)%3B%0Aconsole.log(components)%3B%20%20%2F%2F%20%20%5B%20255%2C%20204%2C%20153%20%5D%0Aconsole.log(components%5B0%5D.toString(16)%2C%20components%5B1%5D.toString(16)%2C%20components%5B2%5D.toString(16))%3B%0A%2F%2F%20ff%20cc%2099%0A%0Afunction%20getColorComponents(color%3A%20number)%3A%20number%5B%5D%20%7B%0A%20%20%20%20%20let%20red%20%3A%20number%20%3D%20color%20%3E%3E%2016%3B%0A%20%20%20%20%20let%20green%3A%20number%20%3D%20color%20%3E%3E%208%20%26%200xFF%3B%0A%20%20%20%20%20let%20blue%3A%20number%20%3D%20color%20%26%200xFF%3B%0A%20%20%20%20%20return%20%5Bred%2C%20green%2C%20blue%5D%3B%0A%7D)
```ts
function getColorComponents(color: number): number[] {
     let red : number = color >> 16;
     let green: number = color >> 8 & 0xFF;
     let blue: number = color & 0xFF;
     return [red, green, blue];
}
```

# 02 戻り値を配列とオブジェクトから選べるようにする
戻り値をオブジェクトで受け取ることもできるようにします。配列かオブジェクトかは、つぎのように関数にブーリアン(論理)値の第2引数を加えて選びます。戻り値がひとつの型に定まりませんので、以下のコード002はとりあえず[`any`](http://fumiononaka.com/Business/html5/FN1701009.html#sec07)で型づけしました。

```ts
let components = getColorComponents(0xFFCC99, false);
console.log(components);  // { red: 255, green: 204, blue: 153 }
```

### [コード002](https://www.typescriptlang.org/play/index.html#src=let%20components%20%3D%20getColorComponents(0xFFCC99%2C%20false)%3B%0Aconsole.log(components)%3B%20%20%2F%2F%20%7B%20red%3A%20255%2C%20green%3A%20204%2C%20blue%3A%20153%20%7D%0A%0Afunction%20getColorComponents(color%3A%20number%2C%20array%3A%20boolean)%3A%20any%20%7B%0A%09let%20red%20%3A%20number%20%3D%20color%20%3E%3E%2016%3B%0A%09let%20green%3A%20number%20%3D%20color%20%3E%3E%208%20%26%200xFF%3B%0A%09let%20blue%3A%20number%20%3D%20color%20%26%200xFF%3B%0A%09if%20(array)%20%7B%0A%09%09return%20%5Bred%2C%20green%2C%20blue%5D%3B%0A%09%7D%20else%20%7B%0A%09%09return%20%7Bred%3A%20red%2C%20green%3A%20green%2C%20blue%3A%20blue%7D%3B%0A%09%7D%0A%7D)
```ts
function getColorComponents(color: number, array: boolean): any {
	let red : number = color >> 16;
	let green: number = color >> 8 & 0xFF;
	let blue: number = color & 0xFF;
	if (array) {
		return [red, green, blue];
	} else {
		return {red: red, green: green, blue: blue};
	}
}
```

# 03 戻り値に納める値もふたつ選べるようにする
カラー値を16進数表記で知りたい場合もあります。数値では表せませんので、文字列を用いることにします。そのため、関数にブーリアン値の第3引数を加えます。以下のコード003は[Union型](https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types)`|`により、変数に数値(`number `)か文字(`string `)という型づけをしました。

```ts
let components = getColorComponents(0xFFCC99, false, true);
console.log(components);  // { red: 'ff', green: 'cc', blue: '99' }
```

### [コード003](https://www.typescriptlang.org/play/index.html#src=let%20components%20%3D%20getColorComponents(0xFFCC99%2C%20false%2C%20true)%3B%20%0Aconsole.log(components)%3B%0A%0Afunction%20getColorComponents(color%3A%20number%2C%20array%3A%20boolean%2C%20string%3A%20boolean)%3A%20any%20%7B%0A%09let%20red%20%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%2016%3B%0A%09let%20green%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%208%20%26%200xFF%3B%0A%09let%20blue%3A%20number%20%7C%20string%20%3D%20color%20%26%200xFF%3B%0A%09if%20(string)%20%7B%0A%09%09red%20%3D%20red.toString(16)%3B%0A%09%09green%20%3D%20green.toString(16)%3B%0A%09%09blue%20%3D%20blue.toString(16)%3B%0A%09%7D%0A%09if%20(array)%20%7B%0A%09%09return%20%5Bred%2C%20green%2C%20blue%5D%3B%0A%09%7D%20else%20%7B%0A%09%09return%20%7Bred%3A%20red%2C%20green%3A%20green%2C%20blue%3A%20blue%7D%3B%0A%09%7D%0A%7D)
```ts
function getColorComponents(color: number, array: boolean, string: boolean): any {
	let red : number | string = color >> 16;
	let green: number | string = color >> 8 & 0xFF;
	let blue: number | string = color & 0xFF;
	if (string) {
		red = red.toString(16);
		green = green.toString(16);
		blue = blue.toString(16);
	}
	if (array) {
		return [red, green, blue];
	} else {
		return {red: red, green: green, blue: blue};
	}
}
```

# 04 オブジェクトを型づけする
関数の戻り値も、`any`を改めて、きちんと型づけしましょう。オブジェクトは型を`Object`にしたのでは、クラスに定義済みでないプロパティが加えられません。そこで、以下のコード004は、関数の戻り値の型づけにオブジェクト型リテラル(「TypeScript入門 04: オブジェクト型リテラルとインタフェースを使う」02「[型定義をつくる](http://fumiononaka.com/Business/html5/FN1609005.html#sec02)」参照)を使いました。なお、配列要素にUnion型を用いるときは、要素の型を丸かっこ`()`でくくります。

### [コード004](https://www.typescriptlang.org/play/index.html#src=let%20components%20%3D%20getColorComponents(0xFFCC99%2C%20false%2C%20true)%3B%20%0Aconsole.log(components)%3B%0A%0Afunction%20getColorComponents(color%3A%20number%2C%20array%3A%20boolean%2C%20string%3A%20boolean)%3A%20%0A%09(number%20%7C%20string)%5B%5D%20%7C%0A%09%7Bred%3A%20number%20%7C%20string%2C%20green%3A%20number%20%7C%20string%2C%20blue%3A%20number%20%7C%20string%7D%0A%7B%0A%09let%20red%20%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%2016%3B%0A%09let%20green%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%208%20%26%200xFF%3B%0A%09let%20blue%3A%20number%20%7C%20string%20%3D%20color%20%26%200xFF%3B%0A%09if%20(string)%20%7B%0A%09%09red%20%3D%20red.toString(16)%3B%0A%09%09green%20%3D%20green.toString(16)%3B%0A%09%09blue%20%3D%20blue.toString(16)%3B%0A%09%7D%0A%09if%20(array)%20%7B%0A%09%09return%20%5Bred%2C%20green%2C%20blue%5D%3B%0A%09%7D%20else%20%7B%0A%09%09return%20%7Bred%3A%20red%2C%20green%3A%20green%2C%20blue%3A%20blue%7D%3B%0A%09%7D%0A%7D)
```ts
function getColorComponents(color: number, array: boolean, string: boolean): 
	(number | string)[] |
	{red: number | string, green: number | string, blue: number | string}
{
	let red : number | string = color >> 16;
	let green: number | string = color >> 8 & 0xFF;
	let blue: number | string = color & 0xFF;
	if (string) {
		red = red.toString(16);
		green = green.toString(16);
		blue = blue.toString(16);
	}
	if (array) {
		return [red, green, blue];
	} else {
		return {red: red, green: green, blue: blue};
	}
}
```

# 05 引数を配列でまとめる
関数に渡すブーリアン値の引数は、あとあとオプションの数が増える場合に備えて、まとめてしまいます。以下のコード005は、引数をブーリアン値の配列にしました。これなら、手直しもさほど多くありません。

```ts
let components = getColorComponents(0xFFCC99, [false, true]);
console.log(components);
```

### [コード005](https://www.typescriptlang.org/play/index.html#src=let%20components%20%3D%20getColorComponents(0xFFCC99%2C%20%5Bfalse%2C%20true%5D)%3B%20%0Aconsole.log(components)%3B%0A%0Afunction%20getColorComponents(color%3A%20number%2C%20format%3A%20boolean%5B%5D)%3A%20%0A%09(number%20%7C%20string)%5B%5D%20%7C%0A%09%7Bred%3A%20number%20%7C%20string%2C%20green%3A%20number%20%7C%20string%2C%20blue%3A%20number%20%7C%20string%7D%0A%7B%0A%09let%20red%20%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%2016%3B%0A%09let%20green%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%208%20%26%200xFF%3B%0A%09let%20blue%3A%20number%20%7C%20string%20%3D%20color%20%26%200xFF%3B%0A%09if%20(format%5B1%5D)%20%7B%0A%09%09red%20%3D%20red.toString(16)%3B%0A%09%09green%20%3D%20green.toString(16)%3B%0A%09%09blue%20%3D%20blue.toString(16)%3B%0A%09%7D%0A%09if%20(format%5B0%5D)%20%7B%0A%09%09return%20%5Bred%2C%20green%2C%20blue%5D%3B%0A%09%7D%20else%20%7B%0A%09%09return%20%7Bred%3A%20red%2C%20green%3A%20green%2C%20blue%3A%20blue%7D%3B%0A%09%7D%0A%7D)
```ts
function getColorComponents(color: number, format: boolean[]): 
	(number | string)[] |
	{red: number | string, green: number | string, blue: number | string}
{
	let red : number | string = color >> 16;
	let green: number | string = color >> 8 & 0xFF;
	let blue: number | string = color & 0xFF;
	if (format[1]) {
		red = red.toString(16);
		green = green.toString(16);
		blue = blue.toString(16);
	}
	if (format[0]) {
		return [red, green, blue];
	} else {
		return {red: red, green: green, blue: blue};
	}
}
```

# 06 複数のフラグを列挙型でまとめる
配列でまとめるとコードは簡単なものの、どの要素が何を決めるのかわかりにくくなります。[列挙型](http://fumiononaka.com/Business/html5/FN1701009.html#sec06)`enum`を使うと、数値が名前で表せます。そして、ビット演算を用いれば、複数のフラグがひとつの整数で表せるのです(「2進数・16進数とビット演算」02「[複数のフラグをひとつの整数で表す](http://www.fumiononaka.com/Business/html5/FN1509001.html#sec02)」)。以下のコード006は、列挙型とビット演算を使って書き替えました。

```ts
console.log(getColorComponents(0xFFCC99));  // { red: 255, green: 204, blue: 153 }
console.log(getColorComponents(0xFFCC99, Format.STRING));  // { red: 'ff', green: 'cc', blue: '99' }
console.log(getColorComponents(0xFFCC99, Format.ARRAY | Format.STRING));  // [ 'ff', 'cc', '99' ]
```

### [コード006](https://www.typescriptlang.org/play/index.html#src=enum%20Format%20%7BARRAY%20%3D%201%2C%20STRING%20%3D%202%7D%3B%0Aconsole.log(Format)%3B%20%20%2F%2F%20%7B%20'1'%3A%20'ARRAY'%2C%20'2'%3A%20'STRING'%2C%20ARRAY%3A%201%2C%20STRING%3A%202%20%7D%0Aconsole.log(getColorComponents(0xFFCC99))%3B%20%20%2F%2F%20%7B%20red%3A%20255%2C%20green%3A%20204%2C%20blue%3A%20153%20%7D%0Aconsole.log(getColorComponents(0xFFCC99%2C%20Format.STRING))%3B%20%20%2F%2F%20%7B%20red%3A%20'ff'%2C%20green%3A%20'cc'%2C%20blue%3A%20'99'%20%7D%0Aconsole.log(getColorComponents(0xFFCC99%2C%20Format.ARRAY%20%7C%20Format.STRING))%3B%20%20%2F%2F%20%5B%20'ff'%2C%20'cc'%2C%20'99'%20%5D%0A%0Afunction%20getColorComponents(color%3A%20number%2C%20format%20%3D%200)%3A%20%0A%09(number%20%7C%20string)%5B%5D%20%7C%0A%09%7Bred%3A%20number%20%7C%20string%2C%20green%3A%20number%20%7C%20string%2C%20blue%3A%20number%20%7C%20string%7D%0A%7B%0A%09let%20red%20%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%2016%3B%0A%09let%20green%3A%20number%20%7C%20string%20%3D%20color%20%3E%3E%208%20%26%200xFF%3B%0A%09let%20blue%3A%20number%20%7C%20string%20%3D%20color%20%26%200xFF%3B%0A%09if%20((format%20%26%202)%20%3D%3D%3D%20Format.STRING)%20%7B%0A%09%09red%20%3D%20red.toString(16)%3B%0A%09%09green%20%3D%20green.toString(16)%3B%0A%09%09blue%20%3D%20blue.toString(16)%3B%0A%09%7D%0A%09if%20((format%20%26%201)%20%3D%3D%3D%20Format.ARRAY)%20%7B%0A%09%09return%20%5Bred%2C%20green%2C%20blue%5D%3B%0A%09%7D%20else%20%7B%0A%09%09return%20%7Bred%3A%20red%2C%20green%3A%20green%2C%20blue%3A%20blue%7D%3B%0A%09%7D%0A%7D)
```ts
enum Format {ARRAY = 1, STRING = 2};
console.log(Format);  // { '1': 'ARRAY', '2': 'STRING', ARRAY: 1, STRING: 2 }
function getColorComponents(color: number, format = 0): 
	(number | string)[] |
	{red: number | string, green: number | string, blue: number | string}
{
	let red : number | string = color >> 16;
	let green: number | string = color >> 8 & 0xFF;
	let blue: number | string = color & 0xFF;
	if ((format & 2) === Format.STRING) {
		red = red.toString(16);
		green = green.toString(16);
		blue = blue.toString(16);
	}
	if ((format & 1) === Format.ARRAY) {
		return [red, green, blue];
	} else {
		return {red: red, green: green, blue: blue};
	}
}
```

# 07 Union型に名前をつける
前掲コード006では、数値(`number `)か文字(`string `)という型づけがあちこちで使われています。[型の別名](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases)(type alias)を用いると、ひとつの名前でまとめてしまえます。以下のコード007のようにわかりやすい名前にすれば、スクリプトも見やすくなります。なお、[配列はジェネリック型で](http://fumiononaka.com/Business/html5/FN1701009.html#sec04)定めました(別の書き方を示しただけですので、とくに大きな違いはありません)。

### [コード007](https://www.typescriptlang.org/play/index.html#src=enum%20Format%20%7BARRAY%20%3D%201%2C%20STRING%20%3D%202%7D%3B%0Atype%20NumberOrString%20%3D%20number%20%7C%20string%3B%0Aconsole.log(Format)%3B%20%20%2F%2F%20%7B%20'1'%3A%20'ARRAY'%2C%20'2'%3A%20'STRING'%2C%20ARRAY%3A%201%2C%20STRING%3A%202%20%7D%0Aconsole.log(getColorComponents(0xFFCC99))%3B%20%20%2F%2F%20%7B%20red%3A%20255%2C%20green%3A%20204%2C%20blue%3A%20153%20%7D%0Aconsole.log(getColorComponents(0xFFCC99%2C%20Format.STRING))%3B%20%20%2F%2F%20%7B%20red%3A%20'ff'%2C%20green%3A%20'cc'%2C%20blue%3A%20'99'%20%7D%0Aconsole.log(getColorComponents(0xFFCC99%2C%20Format.ARRAY%20%7C%20Format.STRING))%3B%20%20%2F%2F%20%5B%20'ff'%2C%20'cc'%2C%20'99'%20%5D%0A%0Afunction%20getColorComponents(color%3A%20number%2C%20format%20%3D%200)%3A%20%0A%09Array%3CNumberOrString%3E%20%7C%0A%09%7Bred%3A%20NumberOrString%2C%20green%3A%20NumberOrString%2C%20blue%3A%20NumberOrString%7D%0A%7B%0A%09let%20red%20%3A%20NumberOrString%20%3D%20color%20%3E%3E%2016%3B%0A%09let%20green%3A%20NumberOrString%20%3D%20color%20%3E%3E%208%20%26%200xFF%3B%0A%09let%20blue%3A%20NumberOrString%20%3D%20color%20%26%200xFF%3B%0A%09if%20((format%20%26%202)%20%3D%3D%3D%20Format.STRING)%20%7B%0A%09%09red%20%3D%20red.toString(16)%3B%0A%09%09green%20%3D%20green.toString(16)%3B%0A%09%09blue%20%3D%20blue.toString(16)%3B%0A%09%7D%0A%09if%20((format%20%26%201)%20%3D%3D%3D%20Format.ARRAY)%20%7B%0A%09%09return%20%5Bred%2C%20green%2C%20blue%5D%3B%0A%09%7D%20else%20%7B%0A%09%09return%20%7Bred%3A%20red%2C%20green%3A%20green%2C%20blue%3A%20blue%7D%3B%0A%09%7D%0A%7D)
```ts
enum Format {ARRAY = 1, STRING = 2};
type NumberOrString = number | string;
console.log(Format);  // { '1': 'ARRAY', '2': 'STRING', ARRAY: 1, STRING: 2 }
function getColorComponents(color: number, format = 0): 
	Array<NumberOrString> |
	{red: NumberOrString, green: NumberOrString, blue: NumberOrString}
{
	let red : NumberOrString = color >> 16;
	let green: NumberOrString = color >> 8 & 0xFF;
	let blue: NumberOrString = color & 0xFF;
	if ((format & 2) === Format.STRING) {
		red = red.toString(16);
		green = green.toString(16);
		blue = blue.toString(16);
	}
	if ((format & 1) === Format.ARRAY) {
		return [red, green, blue];
	} else {
		return {red: red, green: green, blue: blue};
	}
}
```