TypeScriptで関数の型を定義する方法について、実践的な例を交えながら詳しく解説します。

基本的な関数の型定義

TypeScriptでは、関数の引数と戻り値の型を明示的に指定することができます。

// 基本的な関数の型定義
function add(a: number, b: number): number {
    return a + b;
}

// アロー関数での型定義
const multiply = (x: number, y: number): number => {
    return x * y;
};

関数型の変数定義

関数型を変数に代入する場合は、以下のように定義します。

// 関数型の変数定義
type MathOperation = (x: number, y: number) => number;

const divide: MathOperation = (a, b) => {
    return a / b;
};

オプショナルパラメータとデフォルト値

引数を省略可能にしたり、デフォルト値を設定したりできます。

// オプショナルパラメータ(?を使用)
function greet(name: string, greeting?: string): string {
    return greeting ? `${greeting}, ${name}!` : `Hello, ${name}!`;
}

// デフォルト値の設定
function calculateTotal(price: number, taxRate: number = 0.1): number {
    return price * (1 + taxRate);
}

関数のオーバーロード

同じ関数名で異なる引数のパターンを定義できます。

// オーバーロードシグネチャ
function processInput(input: number): number;
function processInput(input: string): string;
function processInput(input: number | string): number | string {
    if (typeof input === "number") {
        return input * 2;
    } else {
        return input.toUpperCase();
    }
}

ジェネリック関数

型パラメータを使用して、柔軟な型定義が可能です。

// ジェネリック関数の定義
function firstElement<T>(arr: T[]): T | undefined {
    return arr[0];
}

// 使用例
const numbers = firstElement([1, 2, 3]); // number型
const strings = firstElement(["a", "b", "c"]); // string型

Rest Parameters

可変長引数を受け取る関数の型定義もできます。

function sum(...numbers: number[]): number {
    return numbers.reduce((total, n) => total + n, 0);
}

コールバック関数の型定義

コールバック関数を引数に取る関数の型定義方法です。

function fetchData(callback: (data: string) => void): void {
    // データ取得の処理
    callback("取得したデータ");
}

async/await関数の型定義

非同期関数の型定義は、Promise型を使用します。

async function fetchUser(id: number): Promise<User> {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
}

型ガード関数

型の絞り込みを行う関数の定義方法です。

function isString(value: unknown): value is string {
    return typeof value === "string";
}

メソッドの型定義

クラス内のメソッドの型定義方法です。

class Calculator {
    add(a: number, b: number): number {
        return a + b;
    }
    
    static multiply(x: number, y: number): number {
        return x * y;
    }
}

以上の型定義パターンを適切に組み合わせることで、型安全な関数を実装できます。エラーの早期発見やコードの保守性向上に役立ちます。

実際の開発では、これらの型定義を状況に応じて使い分けることが重要です。特に、ジェネリックやオーバーロードは、柔軟で再利用可能なコードを書く際に非常に有用です。