サイトトップ

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

HTML5テクニカルノート

TypeScriptハンドブック 02: 変数と関数およびオブジェクトの型


「TypeScriptハンドブック」シリーズ第2回は、「Everyday Types」のページから以下の項目について解説します(かっこ内は原文の項目タイトル)。

01 変数の型注釈

constletで変数を宣言するとき、型注釈が加えられます。変数名のあと、コロン(:)に続けて定めるのがデータ型です。つぎのコードは変数(myName)をstringで型づけし、初期値の文字列(Alice)を与えました。


let myName: string = "Alice";

多くの場合、型注釈は加えなくて構いません。TypeScriptが推論できるときは、自動的に型づけされるからです。上のコード例で型注釈がなくても、初期値から型はstringだと推論されます。


let myName = "Alice";
// エラー: 型 'number' を型 'string' に割り当てることはできません。
// myName = 7;

推論の仕組みについては、とくに初心者であれば理解する必要はありません。型推論が働くことだけ頭においてコードを書いていくと、思いのほか型注釈は省けることに気づくでしょう。

02 関数

JavaScriptでデータを受け渡すために、おもに用いられるのが関数(function)です。数学の関数と同じく、入力と出力があります(プログラミングではいずれか、あるいは両方とも省くことができます)。それぞれに対するTypeScriptの型づけについてご説明しましょう。

図001■関数

図001

(Wikimedia Commonsより)

02-01 引数の型注釈

関数を宣言するとき、引数に型注釈を加えて、それぞれの受け取るデータ型が定められます。変数と同じように、引数名のあとに型注釈を添えてください。関数呼び出しに渡される引数に対して、型注釈にもとづくチェックが行われます。


function greet(name: string) {
	console.log("Hello, " + name.toUpperCase() + "!!");
}
greet("Alice");  // Hello, ALICE!!
// 型 'number' の引数を型 'string' のパラメーターに割り当てることはできません。
// greet(7);

なお、引数に型注釈がなくても、TypeScriptは渡された引数の数が合っているかは確かめます。

02-02 戻り値の型注釈

戻り値の型注釈を加えるのは、引数の閉じかっこ())のあとです。変数や引数と同じく、コロン(:)のあとにデータ型を加えてください。


function getDiceNumber(): number {
	const diceNumber = Math.floor(Math.random() * 6) + 1;
  return diceNumber;
}

変数の場合と同じく、関数の戻り値も型注釈はたいてい省けます。関数本体の文脈(return文)から、TypeScriptが返される値の型を推論できるからです。したがって、上のコード例で、戻り値の型注釈は書かなくて差し支えありません。

あえてドキュメント化の目的で、戻り値の型を示すことはあります。あるいは、関数を修正したとき、うっかり戻り値の型を変えてしまうことが防げるでしょう。

02-03 無名関数

無名関数は、宣言された関数とは少し異なるところがあります。無名関数の呼び出される文脈によっては、TypeScriptが引数のデータ型を推論によって決められるからです。たとえば、つぎの配列の変数(names)は型注釈がなくても、TypeScriptは型を推論できます。


const names = ["Alice", "Bob", "Eve"];

すると、その配列(names)のメソッド(forEach())を呼び出して要素(name)が取り出されるとき、それぞれの型は注釈なしでも配列の変数に対する推論から決まるのです。要素のデータ型に備わるプロパティやメソッドは、そのまま参照して構いません。逆に、要素の型にないアクセスには、エラーが示されます。


names.forEach((name) => {
	// string型の備えるメソッドにアクセスできる。
	console.log(name.toUpperCase());
	// プロパティ 'toUppercase' は型 'string' に存在していません。'toUpperCase' ですか?
	// console.log(name.toUppercase());
});

03 オブジェクト型

プリミティブは別にすると、もっともよく用いられる型はオブジェクトです。JavaScripでプロパティをもつデータのほとんどがオブジェクト型に含まれます。オブジェクトの型を定めるのは、プロパティとその型の組み合わせです。JavaScriptのオブジェクトリテラルと同じく、波かっこ({})の中にプロパティと型をコロン(:)で組み合わせ、カンマ(,)区切りで加えてください。なお、区切り記号にはセミコロン(;)も使え、最後のプロパティの後ろに区切り記号をつけても構いません(「末尾のカンマ」参照)。


function printCoord(point: { x: number, y: number }) {
	console.log(`(x, y): (${point.x}, ${point.y})`);
}
printCoord({ x: 3, y: 4 });  // (x, y): (3, 4)

プロパティ名のみ定めて、型は省くこともできます。その場合、プロパティの型とみなされるのはanyです。

任意のプロパティ

任意のプロパティ(optional properties)というのは、オブジェクト型のプロパティのうち省いても構わない値です。プロパティ名のあとに疑問符(?)を添えると、省略できるようになります。任意のプロパティには、値を与えなくてもエラーになりません。


function printName(name: { first: string; last?: string }) {
	console.log(`first: ${name.first}, last: ${name.last}`)
}
printName({ first: "Alice", last: "Liddell" });  // first: Alice, last: Liddell
printName({ first: "Bob" });  // first: Bob, last: undefined

また、値を与えなかった任意のプロパティは、参照してもエラーは示さず、undefinedが返されることにご注意ください。つまり、型に備わるプロパティやメソッドを参照しようとしても、undefinedの場合にはできないということです。任意のプロパティがundefinedでないことを確かめてから、アクセスしなければなりません。


function printName(name: { first: string; last?: string }) {
	// オブジェクトは 'undefined' である可能性があります。
	// console.log(name.last.toUpperCase());
	if (name.last !== undefined) {
		console.log(name.last.toUpperCase());
	}
}

ここで、ECMAScriptの新しい構文をふたつご紹介しましょう。ひとつは、ECMAScript 2018で採り入れられたオブジェクトの分割代入です(「分割代入を使う」参照)。つぎのように関数の引数に用いると、受け取ったオブジェクトから必要なプロパティを直接取り出せます。もうひとつは、オプショナルチェイニング演算子?.で、こちらの仕様はまだStage 4の提案段階です。?.で参照したデータがnullundefinedの場合ただちにundefinedを返し、ドット(.)のあとの存在しないプロパティにはアクセスしません。TypeScriptではいち早く新しい構文が使えるようになりました。


function printName({ first, last }: { first: string; last?: string }) {
	console.log(last?.toUpperCase());
}

なお、このコード例では引数から取り出した任意のプロパティ(last)が与えられなかったとき、コンソールにはundefinedが出力されます。けれど、前の例の関数はプロパティがundefinedなら、if条件によりconsole.log()は呼び出されません。その点だけ異なることにご注意ください。


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


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