サイトトップ

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

HTML5テクニカルノート

TypeScriptハンドブック 03: ユニオン型


「TypeScriptハンドブック」シリーズ第3回で採り上げるのは、「Everyday Types」のページから、つぎのとおりユニオン型です(かっこ内は原文の項目タイトル)。TypeScriptでは、すでにある型からさまざまな演算子を用いて、新たな型がつくれます。ユニオン型(演算子|)は、その中のひとつです。

01 ユニオン型

ユニオン型(union type)は、共用体型と呼ばれることもあります。複数の型を演算子|で結び、そのうちのいずれかひとつを示す型です。演算子|で結ばれたそれぞれの型を、ユニオンのメンバーといいます。

01-01 ユニオン型を定める

つぎの関数(printId())は、引数(id)をnumberstringのユニオン型としました。引数に渡せるのは、数値か文字列だけです。それ以外の値は受け取りません。


function printId(id: number | string) {
	console.log("Your ID is: " + id);
}
printId(101);  // Your ID is: 101
printId("202");  // Your ID is: 202
// 型 'string[]' の引数を型 'string | number' のパラメーターに割り当てることはできません。
// 型 'string[]' を型 'string' に割り当てることはできません。
// printId(["202"]);

ノート01■数値の配列を渡したときのエラー

前掲の関数(printId())に、数値の配列を渡したとき、エディタ(Visual Studio Code)に示されるエラーが正しくないようです。


// 型 'number' を型 'string' に割り当てることはできません。
printId([303]);

それでも実行すすれば、コンソールには正しいエラーメッセージが表れます。

Argument of type 'number[]' is not assignable to parameter of type 'string | number'. Type 'number[]' is not assignable to type 'string'.

01-02 ユニオン型を使う

ユニオン型は、メンバーのいずれかの型と合致すれば、値を受け取ります。では、ユニオン型の値を使うときはどうでしょう。前掲の関数(printId())を書き替え、引数(id)に対してStringオブジェクトのメソッド(String.prototype.substr())を呼び出そうとしたのが以下のコードです。

ユニオン型の値そのままのとき、TypeScriptはすべてのメンバーが備えるプロパティやメソッドの参照しか許しません。そのため、ユニオン型string | numberの値に、numberがもたないメソッドは呼び出せないのです。


function printId(id: number | string) {
	// プロパティ 'substr' は型 'string | number' に存在しません。
	// プロパティ 'substr' は型 'number' に存在しません。
	console.log("Your ID is: " + id.substr(-2));
}

こういうときは、コードで論理的に値を絞り込みます。TypeScriptはその論理構造にしたがって、値の型をより具体的に推論するのです。

今回のユニオン型であれば、typeof演算子でstringの場合をif文により切り分けられます。すると、切り分けた処理の中では、TypeScriptは値をstring型として扱うことになるのです。これは、型づけのない標準JavaScriptでも行っていることでしょう。 また、number | stringのユニオン型でstringを切り分けたのですから、else文の値はTypeScriptがnumberとみなします。


function printId(id: number | string) {
	if (typeof id === "string") {
		console.log("Your ID is: " + id.substr(-2));
	} else {
		console.log("Your ID is: " + id % 100);
	}
}
printId(101);  // Your ID is: 1
printId("202");  // Your ID is: 02

関数の引数に、ひとつの型とその配列を受け取らせたいときも、ユニオン型で表せます。つぎの関数(welcomePeople())の引数(names)は、string[]stringのユニオン型です。ふたつの切り分けには、Array.isArray()メソッドを用いればよいでしょう。


function welcomePeople(names: string[] | string) {
	if (Array.isArray(names)) {
		console.log("Hello, " + names.join(" and "));
	} else {
		console.log("Welcome lone traveler " + names);
	}
}
welcomePeople(["Alice", "Bob", "Eve"]);  // Hello, Alice and Bob and Eve
welcomePeople("Alice");  // Welcome lone traveler Alice

ユニオン型の複数のメンバーが、たまたま同じプロパティやメソッドを備えている場合もあるでしょう。その場合は、値の切り分けはしなくて構いません。たとえば、つぎの関数(getFirstThree())は、引数(x)の配列と文字列に共通するプロパティとメソッドを参照した例です。


function getFirstThree(x: number[] | string) {
	return {firstThree: x.slice(0, 3), length: x.length};
}
console.log(getFirstThree([0, 1, 2, 3, 4]));  // {firstThree: [0, 1, 2], length: 5}
console.log(getFirstThree("01234"));  // {firstThree: "012", length: 5}


作成者: 野中文雄
作成日: 2021年04月17日


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