メインコンテンツまでスキップ

関数の基本

この章では、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 = valueparam?
省略時の値指定したデフォルト値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}!`;
}

試してみよう: 計算ユーティリティを作ろう ★

以下の要件を満たす計算ユーティリティ関数群を作成してください。

要件:

  1. add(a, b): 2つの数値を足し算する関数
  2. subtract(a, b): 2つの数値を引き算する関数
  3. multiply(a, b): 2つの数値を掛け算する関数(bのデフォルト値は1)
  4. divide(a, b): 2つの数値を割り算する関数(bが0の場合はnullを返す)
  5. calculate(a, b, operation): 2つの数値と演算の種類('add', 'subtract', 'multiply', 'divide')を受け取り、計算結果を返す関数
ヒント
  1. すべての関数で引数と戻り値に型注釈を付ける
  2. multiplyではデフォルトパラメータを使う
  3. divideでは0除算をチェックし、戻り値の型はnumber | null
  4. calculateでは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.jsonnoImplicitAny: 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です。

解決策: 戻り値が必要な場合は、適切な戻り値の型を定義してください。


次に読む