【TypeScript基礎学習5】関数の型について理解する

JavaScript

こんにちは、かつコーチです。

TypeScriptでは、関数の型定義は非常に重要で、

関数の引数や戻り値に対して正確な型注釈を行うことで、

型安全性を保ちながらコードを書くことができます。

ここでは、TypeScriptにおける関数の型に関連する詳細な内容を解説します。

関数の型注釈(Function Type Annotations)

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

これにより、間違った型の値を引数として渡したり、

意図しない型を戻り値として返したりするミスを防ぐことができます。

基本的な関数の型注釈

関数の型注釈は、引数の型と戻り値の型を指定することで行います。

function add(x: number, y: number): number {
  return x + y;
}
  • x: numbery: number: 引数が数値型であることを指定しています。
  • : number: 関数の戻り値が数値型であることを指定しています。

この型注釈により、例えば文字列を引数として渡そうとすると、TypeScriptはエラーを報告します。

add("hello", 5); // エラー: 引数 'hello' は型 'number' に割り当てることができません。

関数型(Function Types)

関数そのものの型もTypeScriptでは定義できます。

関数の型定義は、引数の型と戻り値の型を記述したシグネチャ(型注釈)を用いて行います。

関数型の基本的な例

let myFunc: (x: number, y: number) => number;

myFunc = function (a: number, b: number): number {
  return a + b;
};

console.log(myFunc(5, 10)); // 15
  • myFunc: (x: number, y: number) => number: これはmyFuncが、2つのnumber型引数を取り、number型を返す関数であることを示します。
  • 型注釈によって、myFuncが常に正しい引数型と戻り値を持つように強制されています。

オプショナルな引数の型

関数の引数をオプションにすることも可能です。

オプショナルな引数は、関数が呼び出されたときにその引数が渡されなかった場合に、

undefinedと見なされます。

function greet(name: string, greeting?: string): string {
  return greeting ? `${greeting}, ${name}!` : `Hello, ${name}!`;
}

console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet("Alice", "Good morning")); // "Good morning, Alice!"
  • greeting?: string: greeting引数はオプションであり、渡されない場合はundefinedが代わりに渡されます。

デフォルト値の型

関数の引数にデフォルト値を指定する場合、その引数の型はデフォルト値の型から推論されます。

デフォルト値が設定されている場合、その引数は必須ではなくなります。

function greet(name: string, greeting: string = "Hello"): string {
  return `${greeting}, ${name}!`;
}

console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet("Alice", "Hi")); // "Hi, Alice!"
  • greeting: string = "Hello": greetingにはデフォルトで"Hello"が設定されており、関数呼び出し時にこの引数を省略した場合でも問題ありません。

レストパラメータの型(Rest Parameters)

レストパラメータは、可変長引数を受け取る関数に使用されます。TypeScriptでは、レストパラメータの型も指定できます。

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

console.log(sum(1, 2, 3, 4)); // 10
console.log(sum(5, 10)); // 15
  • ...numbers: number[]: レストパラメータnumbersは、任意の数の数値を受け取り、それらをnumber[]型の配列として扱います。

無名関数(Anonymous Functions)やアロー関数の型

無名関数やアロー関数の型は、通常の関数型と同じように定義できます。

特にアロー関数は簡潔に記述できるため、よく使用されます。

無名関数の型

let multiply: (x: number, y: number) => number = function (a: number, b: number): number {
  return a * b;
};

console.log(multiply(3, 4)); // 12

アロー関数の型

アロー関数の型も同様に定義できます。

let multiply: (x: number, y: number) => number = (a, b) => a * b;

console.log(multiply(3, 4)); // 12
  • アロー関数の場合、TypeScriptは引数abの型を推論しますが、明示的に型を記述することも可能です。

関数のオーバーロード(Function Overloads)

TypeScriptでは、関数のオーバーロードを使って、

同じ関数名でも異なる引数の組み合わせに対応した複数のシグネチャを定義できます。

関数オーバーロードの例

function double(value: string): string;
function double(value: number): number;
function double(value: any): any {
  if (typeof value === "number") {
    return value * 2;
  } else if (typeof value === "string") {
    return value.repeat(2);
  }
}

console.log(double(10)); // 20
console.log(double("abc")); // "abcabc"
  • ここでは、double関数が数値の場合と文字列の場合で異なる処理を行うことができます。
  • 関数の本体では、valueの型を明示的にanyにし、適切な型チェックを行うことで異なる型に対応しています。

コールバック関数の型

関数が他の関数を引数として受け取る場合、コールバック関数の型も定義する必要があります。

function processInput(input: string, callback: (output: string) => void): void {
  const result = input.toUpperCase();
  callback(result);
}

processInput("hello", (output) => {
  console.log(output); // "HELLO"
});
  • callback: (output: string) => void: コールバック関数は文字列outputを受け取り、戻り値を返さない(void)関数であることを示しています。

9. ジェネリック関数(Generic Functions)

ジェネリック関数は、型をパラメータとして受け取る関数です。

これにより、異なる型に対して同じ処理を行う汎用的な関数を定義できます。

ジェネリック関数の例

function identity<T>(arg: T): T {
  return arg;
}

console.log(identity<string>("Hello")); // "Hello"
console.log(identity<number>(42)); // 42

複数の型引数を使うジェネリック関数

複数の型引数を使うことも可能です。

function map<K, V>(key: K, value: V): [K, V] {
  return [key, value];
}

console.log(map("id", 123)); // ["id", 123]
  • map<K, V>: 2つの型引数KVを受け取り、それに応じた型のタプルを返す関数を定義しています。

まとめ

TypeScriptでは、関数に型注釈を付けることで、型安全なコードを書けるようになります。

引数や戻り値、コールバック、オプショナル引数、デフォルト値、レストパラメータ、

関数オーバーロード、ジェネリック関数など、さまざまな型定義の方法が用意されており、

それらを活用することでより堅牢で可読性の高いコードが書けます。

タイトルとURLをコピーしました