TypeScript

TypeScriptの関数入門:引数・戻り値・オーバーロードの型を指定する

この記事でわかること

TypeScriptの関数の引数と戻り値、型推論、関数型、オプション引数、デフォルト引数、残余引数、オーバーロードを実行できるコードで解説します。

TypeScriptの関数では、引数に受け取る値と戻り値の型を表せます。戻り値の型注釈を省略しても、多くの場合は関数の実装から推論されます。省略しただけで必ず any になるわけではありません。

この記事では、型注釈、関数型、オプション引数、デフォルト引数、残余引数、オーバーロードを順に確認します。各節の例は独立したファイルです。インストール手順で用意する、ローカルのTypeScriptとNode.jsの環境を使います。コードはTypeScript 4.9.4の strict 設定で確認しています。

関数の型を定義する

引数と戻り値の型を指定する

引数の型は引数名の後ろに、戻り値の型は引数リストの後ろに書きます。次の例を src/functions-basic.ts に保存します。

export {};

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

function multiply(a: number, b: number) {
  return a * b;
}

function report(message: string): void {
  console.log(message);
}

const total: number = add(3, 4);
const product: number = multiply(3, 4);
report(`合計: ${total}, 積: ${product}`);
// 合計: 7, 積: 12

multiply の戻り値は、数値同士の計算から number と推論されます。戻り値を明記すると、関数の契約を読み取りやすくなり、意図せず返す値を変えた場合にも気づけます。型推論と明示的な注釈を使い分けましょう。公式の戻り値の型注釈の説明

reportvoid は、呼び出し側が結果の値を利用しない関数を表します。この実装は return で値を返さないため、JavaScriptでの呼び出し結果は undefined になります。

関数そのものの型を書く

関数を変数や引数として扱う場合は、引数リストと => を使った関数型を書けます。次の src/function-types.ts は、計算方法を引数として渡す例です。

export {};

type Calculation = (left: number, right: number) => number;

const subtract: Calculation = (left, right) => left - right;

function calculate(operation: Calculation): number {
  return operation(10, 3);
}

console.log(calculate(subtract)); // 7

subtract の引数は、代入先の Calculation から number と推論されます。関数型を使うと、コールバックが受け取る値と返す値をまとめて表せます。

オプション引数とデフォルト引数

? を付けた引数は省略できます。関数の内部では undefined の可能性を考慮します。src/optional-parameters.ts を作成します。

export {};

function describeCount(count?: number): string {
  if (count === undefined) {
    return "件数は未指定です";
  }
  return `${count}件です`;
}

function repeatMessage(message: string, times = 1): string {
  if (!Number.isInteger(times) || times < 0) {
    throw new RangeError("timesには0以上の整数を指定してください");
  }
  return Array.from({ length: times }, () => message).join(" / ");
}

console.log(describeCount()); // 件数は未指定です
console.log(describeCount(0)); // 0件です
console.log(repeatMessage("確認")); // 確認
console.log(repeatMessage("確認", 0)); // 空文字列
console.log(repeatMessage("確認", 2)); // 確認 / 確認

if (count) だけでは 0 も偽と判定されます。省略と 0 を区別するには count === undefined のように確認します。デフォルト値は、引数を省略した場合または undefined を渡した場合に使われます。null0 には適用されません。

number は整数や非負数だけに限定された型ではありません。例では、回数として妥当かどうかを実行時にも検証しています。

残余引数で可変長の値を受け取る

残余引数は引数リストの最後に置き、渡された値を配列で受け取ります。src/rest-parameters.ts の例です。

export {};

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

console.log(sum()); // 0
console.log(sum(2, 4)); // 6
console.log(sum(2, 4, 6)); // 12

オーバーロードは複数の呼び出し方を表す

オーバーロードは、複数の型シグネチャと一つの実装で定義します。TypeScriptが実行時に別々の関数を選ぶ仕組みではありません。実際の分岐は実装に書きます。src/overloads.ts に保存します。

export {};

function duplicate(value: string): string;
function duplicate(value: number): number;
function duplicate(value: string | number): string | number {
  if (typeof value === "string") {
    return value + value;
  }
  return value * 2;
}

console.log(duplicate("TS").toLowerCase()); // tsts
console.log(duplicate(4).toFixed(1)); // 8.0

呼び出し側から利用できるのは、実装より上に宣言したシグネチャです。実装側が string | number を受け取れても、その型の変数をそのまま渡せるオーバーロードは、この例では宣言していません。戻り値を引数別に区別する必要がなければ、ユニオン型の引数を持つ一つの関数で十分な場合もあります。公式のオーバーロードの説明

サンプルを実行する

例えば最初の例は、プロジェクトのルートから次のコマンドで実行できます。ほかの節も入力ファイル名と出力ファイル名を対応させて変更してください。

npx tsc src/functions-basic.ts --strict --target ES2020 --module NodeNext --outDir dist --noEmitOnError
node dist/functions-basic.js

ブラウザーに依存しない例なので、Node.jsで結果を確認できます。外部から取得したデータに対しては、型注釈だけでなく値の検証も必要です。

関数の型を実務で使う判断基準

公開する関数には契約を示す

複数ファイルから使う関数では、引数と戻り値を読めば利用方法が分かるようにします。内部の短いコールバックは型推論を使い、同じ説明を何度も記述しないようにできます。

オーバーロードを増やす前に整理する

入力と出力の対応が変わらないなら、ユニオン型の引数で十分なことがあります。呼び出し形式だけを増やすと実装の分岐やテストも増えるため、利用側でどの型情報が必要かを先に決めます。

関連記事

TypeScriptとは・学習順・目的別の記事一覧へ戻る

y.
WRITTEN BY

y_ymo10

SEの部屋で、JavaScript・TypeScript・React.js・Next.jsの開発ノートを公開しています。

ほかの開発ノートを読む →