07. 関数の型

引数・戻り値・オーバーロード・コールバック

📅 作成: 2026-08-29 / 更新: 2026-08-29

この章の到達点

この章の内容

  1. 引数と戻り値
  2. 省略可能引数とデフォルト値
  3. オーバーロード
  4. コールバックの型

07.1 引数と戻り値

書き方

// 関数宣言
function add(a: number, b: number): number {
	return a + b;
}

// アロー関数
const sub = (a: number, b: number): number => a - b;

// 型だけを定義する
type BinaryOp = (a: number, b: number) => number;
const mul: BinaryOp = (a, b) => a * b;      // 引数の型は推論される

型の位置で使う矢印は =>、値としてのアロー関数も => です。

紛らわしいですが、型の文脈では戻り値の型を書き、値の文脈では処理を書きます。type BinaryOp = (a: number, b: number) => number;number は「戻り値の型」です。

戻り値の型は書くべきか

推論に任せられますが、公開する関数には書くことを勧めます。

判定場面理由
書く他のファイルから使われる関数実装を変えたとき、変更の影響がその関数の中で止まる
書く再帰する関数推論できない場合がある
省くその場で使う短いコールバック書くとかえって読みにくい
省く1行で値を返すだけの関数推論で十分
// 書いておくと、実装ミスがこの関数の中で捕まる
function findName(id: string): string {
	const users: Record<string, string> = { "1": "Alice" };
	return users[id];
	//     ~~~~~~~~~~
	// strict では error TS2322: Type 'string | undefined' is not assignable to type 'string'.
	//
	// 訳: 型 'string | undefined' を型 'string' に割り当てることはできません。
}

戻り値の型を書かなければ、この関数の型は string | undefined と推論され、問題は呼び出し側で表面化します。原因から離れた場所でエラーになります。

void と undefined

function log(msg: string): void {
	console.log(msg);
}
意味
void戻り値を使わない。何を返してもよいが、呼び出し側は使ってはいけない
undefinedundefined を返す。return; または return undefined; が必要
never戻ってこない。必ず例外を投げるか、無限ループする
function fail(message: string): never {
	throw new Error(message);
}

never08. 型の絞り込み の網羅性チェックでも使います。

07.2 省略可能引数とデフォルト値

3つの書き方

// ① 省略可能(undefined になりうる)
function greet1(name: string, title?: string) {
	return title ? `${title} ${name}` : name;
}

// ② デフォルト値(省略すると既定値が入る)
function greet2(name: string, title = "様") {
	return `${title} ${name}`;
}

// ③ 可変長引数
function join(sep: string, ...parts: string[]) {
	return parts.join(sep);
}

省略可能引数は末尾にしか置けません。

function f(a?: string, b: number) はエラーです。JavaScript の引数は位置で決まるため、途中を飛ばして渡せないためです。引数が増えてきたらオブジェクトで受け取る形に変えてください。

function createUser(opts: { name: string; age?: number; email?: string }) { /* ... */ }

createUser({ name: "Alice", email: "a@example.com" });   // 順序に縛られない

デフォルト値があると型が変わる

function f(title = "様") {
	// title の型は string(string | undefined ではない)
	return title.length;
}

f();              // OK
f(undefined);     // OK — undefined を渡すと既定値が使われる

動かしてみる

// args.ts
function greet(name: string, title = "様"): string {
	return `${title} ${name}`;
}

function join(sep: string, ...parts: string[]): string {
	return parts.join(sep);
}

console.log(greet("田中"));
console.log(greet("Alice", "Ms."));
console.log(join(" / ", "a", "b", "c"));
$ npx tsx args.ts
様 田中
Ms. Alice
a / b / c

07.3 オーバーロード

実装は1つだけ

TypeScript のオーバーロードは「シグネチャを複数書き、実装は1つ」という形です。

// シグネチャ(呼び出し側から見える形)
function parse(input: string): string[];
function parse(input: number): number[];

// 実装(呼び出し側からは見えない)
function parse(input: string | number): string[] | number[] {
	if (typeof input === "string") {
		return input.split(",");
	}
	return [input];
}

const a = parse("x,y");    // string[]
const b = parse(5);        // number[]

Java・C# から来た人へ

Java・C# のオーバーロードは別々のメソッドが複数存在し、コンパイラが呼び分けます。TypeScript では関数は1つだけで、型の上で複数の見え方を提供しているにすぎません。

実装側で自分で分岐を書く必要があります。シグネチャと実装が食い違っていても、実装側の型が緩ければエラーになりません。書くときは注意してください。

多くの場合は union で足りる

オーバーロードは引数と戻り値の組み合わせが連動するときにだけ必要です。

// オーバーロードは不要 — union で書ける
function double(x: number | string): string {
	return typeof x === "number" ? String(x * 2) : x + x;
}
状況書き方
戻り値の型が引数によって変わるオーバーロード、またはジェネリクス(09
戻り値の型は同じunion で受ける
引数の数が違うだけ省略可能引数・デフォルト値

07.4 コールバックの型

引数は少なくてよい

05. 構造的型付けで触れたとおり、要求より引数が少ない関数は渡せます。

const nums = [1, 2, 3];

nums.map((x) => x * 2);              // OK
nums.map((x, i) => x * i);           // OK — 2つ目も受け取れる
nums.map((x, i, arr) => arr.length); // OK — 3つ目まである
map が渡すもの ① 要素 ② 添字 ③ 配列全体 常に3つ渡される (x) => ...  1つだけ受け取る (x, i) => ...  2つ受け取る (x, i, arr) => ...  3つ受け取る (x, i, arr, extra) => ...  4つ目はエラー OK OK OK × 少ない分には通る。多い分は「渡されない値」を要求するのでエラーになる
図 1 — 引数は少ない分には通る。渡される数より多くは受け取れない

これは JavaScript の性質に合わせた仕様です。

JavaScript では、渡された引数を無視できます。map は常に3つの引数を渡しますが、受け取る側は必要な数だけ書けばよいという書き方が普通です。逆に多すぎるとエラーになります(受け取れない値を要求しているため)。

コールバックの戻り値が void のとき

type Handler = (x: number) => void;

const h: Handler = (x) => x * 2;     // OK。数値を返しているが通る

void「戻り値を使わない」という意味なので、何を返しても構いません。ただし呼び出し側は戻り値を使えません。

この性質が事故になることがあります。

const ids: string[] = [];
const users = [{ id: "1" }, { id: "2" }];

// forEach のコールバックは void を返す想定
users.forEach((u) => ids.push(u.id));   // push は number を返すが、無視される(OK)

これは問題ありません。しかしasync なコールバックを forEach に渡すと、待たれずに進みます。この扱いは 11. 非同期とエラー処理 で扱います。

関数を引数に取る関数を書く

// callback.ts
type Mapper<T, U> = (item: T, index: number) => U;

function mapAll<T, U>(items: T[], fn: Mapper<T, U>): U[] {
	const out: U[] = [];
	for (let i = 0; i < items.length; i++) {
		out.push(fn(items[i], i));
	}
	return out;
}

console.log(mapAll([1, 2, 3], (n) => n * 10));
console.log(mapAll(["a", "b"], (s, i) => `${i}:${s}`));
$ npx tsx callback.ts
[ 10, 20, 30 ]
[ '0:a', '1:b' ]

<T, U> の記法はジェネリクスです。09. ジェネリクス で扱います。

this の型

コールバック内の this が問題になる場合、第1引数として this の型を宣言できます。

function handler(this: HTMLElement, event: Event) {
	console.log(this.tagName);      // this の型が分かる
}

この this引数ではありません。型検査のためだけの宣言で、呼び出し側は渡しません。アロー関数を使えば this の問題自体が起きないため、必要になる場面は限られます。