関数の基本
この章では、TypeScriptにおける関数の基本的な書き方を学びます。
この章で学ぶこと
- 関数の型注釈の書き方
- アロー関数の構文と使い方
- 関数式の基本
- デフォルトパラメータとオプショナルパラメータ
- void型の関数
この章のコード例は、TypeScript Playgroundで試すことができます。
関数の型注釈
TypeScriptでは、関数の引数と戻り値に型を指定できます。これにより、関数の使い方が明確になり、呼び出し時のミスをコンパイル時に検出できます。
基本的な構文
// 関数の型注釈の基本構文
function 関数名(引数名: 引数の型): 戻り値の型 {
// 処理
return 戻り値;
}
具体例
// 2つの数値を足し算する関数
function add(a: number, b: number): number {
return a + b;
}
// 使用例
console.log(add(5, 3)); // 8
console.log(add(10, 20)); // 30
// 型エラーの例(コンパイル時にエラーになる)
// console.log(add('5', '3'));
// Error: Argument of type 'string' is not assignable to parameter of type 'number'
コード解説:
a: number- 第1引数aはnumber型b: number- 第2引数bはnumber型: number(関数名の後)- 戻り値の型はnumber型- 型が一致しない呼び出しはコンパイルエラーになる
戻り値の型推論
TypeScriptは戻り値の型を自動的に推論できます。
// 明示的に戻り値の型を指定
function add1(a: number, b: number): number {
return a + b;
}
// 戻り値の型を省略(型推論に任せる)
function add2(a: number, b: number) {
return a + b; // 戻り値の型はnumberと推論される
}
// どちらも同じように動作する
console.log(add1(5, 3)); // 8
console.log(add2(5, 3)); // 8
ベストプラクティス:
// 推奨: 引数には必ず型注釈を付ける
function multiply(a: number, b: number) {
return a * b;
}
// 複雑な関数では戻り値の型も明示する(可読性向上)
function complexCalculation(x: number, y: number): number {
const step1 = x * 2;
const step2 = y + 10;
const result = step1 + step2;
return result;
}
// 非推奨: 引数に型注釈がないとany型になる
function bad(a, b) { // aとbはany型
return a + b;
}
void型の関数
戻り値がない関数はvoid型を返します。
// 戻り値がない関数(コンソールに出力するだけ)
function greet(name: string): void {
console.log(`Hello, ${name}!`);
// return文がない、またはreturn;だけ
}
greet('Alice'); // 'Hello, Alice!'
// voidを返す関数の戻り値は使用できない
const result = greet('Bob');
console.log(result); // undefined
void型の実用例:
// データを保存する関数(戻り値は必要ない)
function saveData(data: string): void {
localStorage.setItem('userData', data);
console.log('Data saved successfully');
}
// ログを出力する関数
function log(level: string, message: string): void {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] [${level}] ${message}`);
}
log('INFO', 'Application started');
log('ERROR', 'Something went wrong');
アロー関数
ES2015で導入された簡潔な関数の書き方です。モダンなTypeScriptコードでは頻繁に使用されます。
基本的な構文
// 従来の関数式
const add1 = function(a: number, b: number): number {
return a + b;
};
// アロー関数(同じ機能)
const add2 = (a: number, b: number): number => {
return a + b;
};
// アロー関数(省略形: 式が1つの場合)
const add3 = (a: number, b: number): number => a + b;
// すべて同じ結果
console.log(add1(5, 3)); // 8
console.log(add2(5, 3)); // 8
console.log(add3(5, 3)); // 8
アロー関数の省略形
// 通常の書き方
const square1 = (x: number): number => {
return x * x;
};
// 省略形: 波括弧とreturnを省略(式が1つの場合)
const square2 = (x: number): number => x * x;
// 型注釈がある場合は括弧が必要
const square3 = (x: number) => x * x;
// 引数が0個の場合は括弧が必要
const getRandom = (): number => Math.random();
console.log(square1(5)); // 25
console.log(square2(5)); // 25
console.log(square3(5)); // 25
console.log(getRandom()); // 0.123... (ランダムな値)
オブジェクトを返す場合
// オブジェクトを返す場合は括弧で囲む
const createUser = (name: string, age: number) => ({
name: name,
age: age
});
// または波括弧を使った通常の書き方
const createUser2 = (name: string, age: number) => {
return {
name: name,
age: age
};
};
console.log(createUser('Alice', 25)); // { name: 'Alice', age: 25 }
console.log(createUser2('Bob', 30)); // { name: 'Bob', age: 30 }
オブジェクトリテラルを直接返す場合、括弧で囲まないと波括弧が関数のブロックと解釈されてエラーになります。
配列メソッドとの組み合わせ
アロー関数は配列のメソッドでよく使われます。
const numbers = [1, 2, 3, 4, 5];
// map: 各要素を2倍にする
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter: 偶数だけを取り出す
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4]
// reduce: 合計を計算
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15
// find: 条件に合う最初の要素を取得
const found = numbers.find((n) => n > 3);
console.log(found); // 4
関数式
関数を変数に代入する方法です。
基本的な関数式
// 関数式の基本構文
const add = function(a: number, b: number): number {
return a + b;
};
console.log(add(5, 3)); // 8
関数の型注釈
変数に対して関数の型を明示もできます。
// 変数の型注釈(関数の型を明示)
const multiply: (a: number, b: number) => number = function(a, b) {
return a * b;
};
console.log(multiply(4, 5)); // 20
関数型の変数
// 関数の型を定義
// 構文: (引数: 型, ...) => 戻り値の型
let operation: (a: number, b: number) => number;
// この型に合う関数を代入できる
operation = function(a, b) {
return a + b;
};
console.log(operation(10, 20)); // 30
// 別の関数を代入
operation = function(a, b) {
return a * b;
};
console.log(operation(10, 20)); // 200
デフォルトパラメータ
パラメータにデフォルト値を設定できます。引数が省略された場合にデフォルト値が使用されます。
// デフォルトパラメータの基本
function greet(name: string, greeting: string = 'Hello'): string {
return `${greeting}, ${name}!`;
}
console.log(greet('Alice')); // 'Hello, Alice!'
console.log(greet('Bob', 'Hi')); // 'Hi, Bob!'
console.log(greet('Charlie', 'Hey')); // 'Hey, Charlie!'
実践的な例
function createUser(
name: string,
age: number = 0,
isActive: boolean = true
) {
return { name, age, isActive };
}
console.log(createUser('Alice'));
// { name: 'Alice', age: 0, isActive: true }
console.log(createUser('Bob', 25));
// { name: 'Bob', age: 25, isActive: true }
console.log(createUser('Charlie', 30, false));
// { name: 'Charlie', age: 30, isActive: false }
デフォルトパラメータと型推論
// デフォルト値から型が推論される
function multiply(a: number, b = 2) { // bの型はnumberと推論
return a * b;
}
console.log(multiply(5)); // 10 (5 * 2)
console.log(multiply(5, 3)); // 15 (5 * 3)
オプショナルパラメータ
パラメータ名の後に?を付けると、そのパラメータは省略可能になります。
// 基本的な使い方
function greet(name: string, greeting?: string): string {
if (greeting) {
return `${greeting}, ${name}!`;
}
return `Hello, ${name}!`;
}
console.log(greet('Alice')); // 'Hello, Alice!'
console.log(greet('Bob', 'Good morning')); // 'Good morning, Bob!'
オプショナルパラメータの注意点
// オプショナルパラメータは必須パラメータの後に配置する
// 正しい: オプショナルパラメータは最後
function correct(a: number, b?: number, c?: number) {
console.log(a, b, c);
}
correct(1); // 1 undefined undefined
correct(1, 2); // 1 2 undefined
correct(1, 2, 3); // 1 2 3
// 間違い: オプショナルパラメータの後に必須パラメータがある
// function wrong(a?: number, b: number) {} // Error
オプショナルパラメータの型
オプショナルパラメータの型は自動的に| undefinedが付加されます。
function printValue(value?: string): void {
// valueの型は string | undefined
console.log(value);
// 使用前に存在チェックが必要
// console.log(value.toUpperCase()); // Error: 'value' is possibly 'undefined'
// 正しい使い方
if (value) {
console.log(value.toUpperCase());
}
// または Optional Chaining
console.log(value?.toUpperCase());
}
printValue(); // undefined, undefined
printValue('hello'); // hello, HELLO, HELLO
デフォルトパラメータ vs オプショナルパラメータ
| 特徴 | デフォルトパラメータ | オプショナルパラメータ |
|---|---|---|
| 構文 | param = value | param? |
| 省略時の値 | 指定したデフォルト値 | undefined |
| undefinedチェック | 不要 | 必要 |
| 用途 | 通常は特定の値を使う | 値があってもなくてもよい |
// デフォルトパラメータ: 省略時に'Hello'を使う
function greet1(name: string, greeting: string = 'Hello'): string {
return `${greeting}, ${name}!`; // greetingは必ず文字列
}
// オプショナルパラメータ: greetingはundefinedの可能性がある
function greet2(name: string, greeting?: string): string {
if (greeting) { // undefinedチェックが必要
return `${greeting}, ${name}!`;
}
return `Hello, ${name}!`;
}
試してみよう: 計算ユーティリティを作ろう ★
以下の要件を満たす計算ユーティリティ関数群を作成してください。
要件:
add(a, b): 2つの数値を足し算する関数subtract(a, b): 2つの数値を引き算する関数multiply(a, b): 2つの数値を掛け算する関数(bのデフォルト値は1)divide(a, b): 2つの数値を割り算する関数(bが0の場合はnullを返す)calculate(a, b, operation): 2つの数値と演算の種類('add', 'subtract', 'multiply', 'divide')を受け取り、計算結果を返す関数
ヒント
- すべての関数で引数と戻り値に型注釈を付ける
multiplyではデフォルトパラメータを使うdivideでは0除算をチェックし、戻り値の型はnumber | nullcalculateではoperationの値で条件分岐する
回答と解説
// 2つの数値を足し算する関数
const add = (a: number, b: number): number => a + b;
// 2つの数値を引き算する関数
const subtract = (a: number, b: number): number => a - b;
// 2つの数値を掛け算する関数(bのデフォルト値は1)
const multiply = (a: number, b: number = 1): number => a * b;
// 2つの数値を割り算する関数(bが0の場合はnullを返す)
const divide = (a: number, b: number): number | null => {
if (b === 0) {
return null; // 0除算を防ぐ
}
return a / b;
};
// 2つの数値と演算の種類を受け取り、計算結果を返す関数
const calculate = (
a: number,
b: number,
operation: 'add' | 'subtract' | 'multiply' | 'divide'
): number | null => {
switch (operation) {
case 'add':
return add(a, b);
case 'subtract':
return subtract(a, b);
case 'multiply':
return multiply(a, b);
case 'divide':
return divide(a, b);
default:
return null;
}
};
// テスト
console.log(add(5, 3)); // 8
console.log(subtract(10, 4)); // 6
console.log(multiply(5, 3)); // 15
console.log(multiply(5)); // 5 (デフォルト値1)
console.log(divide(10, 2)); // 5
console.log(divide(10, 0)); // null
console.log(calculate(10, 5, 'add')); // 15
console.log(calculate(10, 5, 'subtract')); // 5
console.log(calculate(10, 5, 'multiply')); // 50
console.log(calculate(10, 5, 'divide')); // 2
console.log(calculate(10, 0, 'divide')); // null
解説:
add,subtract: 基本的な関数で、アロー関数の省略形を使用multiply: デフォルトパラメータb = 1を使用し、第2引数を省略可能にdivide: 戻り値の型をnumber | nullとして0除算対策calculate: リテラル型のUnion'add' | 'subtract' | 'multiply' | 'divide'を使用して、有効な演算のみを受け付ける
まとめ
この章で学んだこと:
- 関数の型注釈: 引数と戻り値に型を指定することで、型安全な関数を定義できる
- アロー関数:
=>を使った簡潔な関数の書き方、配列メソッドとの相性が良い - 関数式: 関数を変数に代入し、関数の型を明示できる
- デフォルトパラメータ:
param = valueで省略時のデフォルト値を設定 - オプショナルパラメータ:
param?で省略可能なパラメータを定義 - void型: 戻り値がない関数を表す
次の章では、関数のオーバーロード、コールバック関数、レストパラメータなど、関数の応用について学びます。
初学者がつまずきやすいポイント
よくある間違い
❌ アロー関数でオブジェクトを返す際に括弧を忘れる
// ❌ 間違い: 波括弧が関数ブロックと解釈される
const createUser = (name: string) => { name: name };
// これは { name: name } を返すのではなく、
// name: というラベルを持つ式文として解釈される
// ✅ 正しい: 括弧で囲んでオブジェクトリテラルとして返す
const createUser = (name: string) => ({ name: name });
// または通常の書き方
const createUser2 = (name: string) => {
return { name: name };
};
原因: {}はJavaScriptでは関数ブロックとオブジェクトリテラルの両方に使われます。
解決策: オブジェクトを直接返す場合は({})で囲んでください。
❌ デフォルトパラメータとオプショナルパラメータの使い分け
// ❌ 間違い: デフォルト値があるのにオプショナル(?)も付ける
function greet(name?: string = 'Guest'): void { // Error
console.log(`Hello, ${name}!`);
}
// ✅ 正しい: デフォルト値があればオプショナルは不要
function greet(name: string = 'Guest'): void {
console.log(`Hello, ${name}!`); // nameは必ずstring
}
// オプショナルパラメータを使う場合(undefinedの可能性あり)
function greet2(name?: string): void {
console.log(`Hello, ${name ?? 'Guest'}!`); // nameはstring | undefined
}
原因: デフォルトパラメータは自動的に省略可能になるため、?は冗長です。
解決策: デフォルト値を設定する場合は?を付けず、= 値のみを使用してください。
❌ オプショナルパラメータを必須パラメータより前に置く
// ❌ 間違い: オプショナルパラメータが先に来ている
function wrong(a?: number, b: number): number {
// Error: A required parameter cannot follow an optional parameter
return (a ?? 0) + b;
}
// ✅ 正しい: 必須パラメータを先に配置
function correct(b: number, a?: number): number {
return (a ?? 0) + b;
}
原因: JavaScriptの関数呼び出しは位置引数なので、オプショナルパラメータは最後である必要があります。
解決策: オプショナルパラメータは必ず必須パラメータの後に配置してください。
❌ 関数の戻り値の型注釈を忘れてanyになる
// ❌ 間違い: noImplicitAnyが無効な場合、引数がany型になる
function add(a, b) { // aとbはany型
return a + b;
}
add('5', '10'); // '510'(文字列結合、意図しない動作)
// ✅ 正しい: 引数に型注釈を付ける
function add(a: number, b: number): number {
return a + b;
}
// add('5', '10'); // Error: 型エラーで検出
原因: TypeScriptのnoImplicitAnyオプションが無効だと、型注釈のない引数はanyになります。
解決策: tsconfig.jsonでnoImplicitAny: trueを設定し、常に型注釈を付けてください。
❌ void関数の戻り値を使おうとする
function logMessage(msg: string): void {
console.log(msg);
}
// ❌ 間違い: void関数の戻り値を使おうとする
const result = logMessage('Hello');
if (result) { // resultはvoid(undefined)
console.log('Success'); // 常に実行されない
}
// ✅ 正しい: void関数は副作用のみを期待
logMessage('Hello');
// 戻り値が必要な場合は適切な型を定義
function formatMessage(msg: string): string {
return `[INFO] ${msg}`;
}
const formatted = formatMessage('Hello');
console.log(formatted); // '[INFO] Hello'
原因: void型の関数は値を返さないため、戻り値はundefinedです。
解決策: 戻り値が必要な場合は、適切な戻り値の型を定義してください。