【入門編】引数に渡す関数の「引数の数(Arity)」を制限する型定義のテクニック – TypeScript コア・型システムの基礎解析バイブル

こんにちは。TypeScriptの世界へようこそ。
今日は、あなたがTypeScriptを使いこなす上で避けては通れない、そして極めれば一気に「型の本質」が見えてくる非常に面白いテーマについてお話ししましょう。

テーマは、関数の「引数の数(Arity:アリティ)」を制御するテクニックです。

「関数に渡すコールバック、引数が多すぎるとバグの原因になりそうだな…」と感じたことはありませんか? TypeScriptの柔軟性を活かしつつ、安全なコードを書くための知恵を、私の経験を交えて丁寧に解説していきますね。ここをクリアすれば、関数の型定義はもう怖くありませんよ。

—

1. なぜ「引数の数」を気にする必要があるのか?

TypeScriptの型システムには、「代入可能性(Assignability)」という非常に重要な概念があります。

実はTypeScriptでは、「定義されている引数より少ない数しか使わない関数」を渡すことは、デフォルトで許可されています。

具体的な例を見てみましょう

// 数値を2つ受け取って計算する関数を期待する型
type Calculator = (a: number, b: number) => void;

const myCalc: Calculator = (a) => {
console.log(a); // 引数bを使わなくてもエラーにならない!
};

これはJavaScriptの「使わない引数は無視すればいい」という実用的な性質を引き継いでいるため、基本的には便利な仕様です。

しかし、APIの設計や複雑なロジックを組む際、「余計な引数を定義して、間違った値を使ってほしくない」という場面が出てきます。例えば、第2引数に「非常に重い処理の結果」や「機密性の高いデータ」を渡す設計の場合、意図しない引数の利用を型レベルで制限したくなるのです。

—

2. 【基本】関数の型定義の基本構造

まずは基本を整理しましょう。TypeScriptで関数の型を定義する際、私たちは「何を受け取り(引数)」、「何を返すか(戻り値)」を約束(コントラクト)として定義します。

/

  • 基本的な関数の型定義
  • @param callback – 数値を1つだけ受け取る関数を期待

/
function processData(callback: (value: number) => void) {
const secretValue = 42;
callback(secretValue);
}

// OK: 引数が1つ
processData((n) => console.log(n));

// OK: 引数を使わない (TypeScriptの標準的な振る舞い)
processData(() => console.log(“Done”));

しかし、ここで 「(n, index) => …」のように、2つ以上の引数を受け取ろうとする関数が渡されたらエラーにしたい。そんな時はどうすればいいでしょうか?

—

3. 【応用】引数の数を制限する「Strict Arity」のテクニック

ここからがTypeScriptの真骨頂、コンパイラAPIの挙動を応用したテクニックです。
関数の「引数の型」だけでなく、「引数の配列の長さ」をチェックする仕組みを作ります。

ジェネリクスとParametersを組み合わせる

TypeScriptには、関数の引数をタプル型(配列のような型)として抽出する `Parameters` という便利な組み込み型があります。これを利用して、引数の数を制限する関数を作ってみましょう。

/

  • 特定の引数の数(Arity)を持つ関数のみを許可する高度な型定義

/
type StrictUnary any> =
Parameters[‘length’] extends 1
? F
: “Error: 引数はちょうど1つである必要があります”;

function strictProcess void>(
callback: F & StrictUnary // 交差型を使って制約をかける
) {
callback(100);
}

// — 実行結果 —

// ✅ OK: 引数が1つ
strictProcess((n) => {
console.log(n);
});

// ❌ エラー: 引数が2つあると、型エラーが発生する!
// Argument of type ‘(n: number, i: number) => void’ is not assignable…
strictProcess((n, i) => {
console.log(n, i);
});

このコードの「魔法」の解説

1. `Parameters[‘length’]`: 関数 `F` の引数がいくつあるかを数値として取得します。
2. `extends 1 ? F : never`: 引数の数が1であればそのままの型を返し、そうでなければエラーを誘発させます。
3. `F & StrictUnary`: 実際の関数の型と、この制約を「合体(交差)」させることで、条件を満たさない場合にTypeScriptが「この型は合いませんよ!」と教えてくれるようになります。

—

4. 初学者が陥りやすい「オプショナル引数」の罠

関数の型を定義する際、`?` を使ったオプショナル引数(あってもなくても良い引数)もよく使われますが、ここには注意が必要です。

// 第2引数がオプショナルな関数型
type SafeCallback = (value: number, index?: number) => void;

const myFunc: SafeCallback = (v, i) => {
// i は number | undefined になる
if (i !== undefined) {
console.log(v, i);
}
};

陥りやすいエラー:
オプショナル引数として定義したものを、実装側で「必ずあるもの」として扱ってしまうとエラーになります。
「引数の数を制限したい」のか「引数があってもなくてもいいようにしたい」のかを明確に区別することが、プロへの第一歩です。

—

5. まとめ:TypeScriptを掌握するための地図

今日のポイントを振り返ってみましょう。

1. 標準の挙動: TypeScriptは「少ない引数」で関数を実装することは許容する(安全だから)。
2. 厳密な制限: `Parameters` と条件付き型(Conditional Types)を使えば、引数の数を厳格に縛ることができる。
3. 設計の意図: なぜ制限したいのか?(誤用を防ぐためか、インターフェースを統一するためか)を常に考える。

最初は「なぜこんなに型が複雑なんだろう」と感じるかもしれません。でも、それはTypeScriptが「実行時のエラーを、コードを書いている瞬間に教えてくれようとしている」優しさの裏返しなんです。

今回紹介した「引数の数の制限」は、ライブラリの開発や大規模なプロジェクトで非常に重宝されるテクニックです。これが理解できれば、あなたはもう初心者ではありません。

一歩ずつ、楽しみながら型を組み立てていきましょう。もし分からないことがあれば、いつでもコードに戻って実験してみてください。型は嘘をつきませんから。

ハッピーハッキング!あなたのTypeScriptライフが、より安全で創造的なものになることを願っています。

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