TypeScript | 関数・クラス・ジェネリクス:関数設計の深化 – thisパラメータの明示

TypeScript
スポンサーリンク

「thisパラメータを明示する」とは何をしているのか

まず言葉の整理からいきます。

TypeScript でいう「thisパラメータ」とは、
関数の一番最初の引数の位置にだけ書ける、特別な仮引数のことです。

function greet(this: { name: string }) {
  console.log(`Hello, ${this.name}`);
}
TypeScript

ここでの this: { name: string } は、
「この関数の中で使われる this{ name: string } 型ですよ」
という宣言だけです。

実際の呼び出し時に this を引数として渡すわけではありません。
this はあくまで call / apply / メソッド呼び出しなどでバインドされます。

thisパラメータを明示する=この関数が前提としている「文脈(コンテキスト)」を型で宣言すること
と捉えてください。


基本形:thisパラメータの書き方と呼び出し方

thisパラメータ付き関数の定義

最小の例からいきます。

function showName(this: { name: string }) {
  console.log(this.name);
}
TypeScript

この関数の中では、this は必ず { name: string } 型として扱われます。

function showName(this: { name: string }) {
  // this: { name: string }
  console.log(this.name.toUpperCase()); // OK
}
TypeScript

thisname がない前提のコードは書けません。
逆に言うと、「name がある前提で安心して書ける」ということです。

thisをバインドして呼び出す

呼び出し側は call / apply / bind などで this を指定します。

const person = { name: "Taro" };

showName.call(person); // Hello, Taro
TypeScript

ここで TypeScript は、

showName は「this: { name: string }」を前提にしている
call に渡された person{ name: string } を満たしているか

をチェックします。

もし足りないプロパティのオブジェクトを渡すと、コンパイルエラーになります。

const obj = { title: "Book" };
// showName.call(obj); // エラー: '{ title: string }' に 'name' がない
TypeScript

どんなときに thisパラメータを「明示すべき」か

1. this に強く依存する「自由関数」を書くとき

クラスのメソッドではなく、単体の関数として this に依存する処理を書くときは、
thisパラメータを明示した方が圧倒的に安全です。

function increment(this: { value: number }) {
  this.value++;
}
TypeScript

この関数は、「value: number を持つオブジェクトにバインドされること」を前提にしています。

const counter = { value: 0 };

increment.call(counter);
console.log(counter.value); // 1
TypeScript

ここでの重要ポイントは、

「this に依存する自由関数を書いたのに、this の型を明示しないと、
どんなオブジェクトでもバインドできてしまい、バグの温床になる」

ということです。

this: { value: number } を明示することで、
「この関数は value を持たないオブジェクトには使えない」という制約を
コンパイル時にかけられます。

2. オブジェクトリテラルのメソッドで this を厳密にしたいとき

オブジェクトリテラルのメソッドでも thisパラメータは使えます。

const counter = {
  value: 0,
  increment(this: { value: number }) {
    this.value++;
  },
};
TypeScript

この書き方の意味は、

increment の中の this は { value: number } である」
「このメソッドを他のオブジェクトにバインドして使うときも、その前提を守れ」

という宣言です。

例えば、increment を別のオブジェクトに借りて使うとき。

const another = { value: 10 };

counter.increment.call(another); // OK
TypeScript

anothervalue: number があるのでOKですが、
value がないオブジェクトを渡すとエラーになります。

const bad = { count: 0 };
// counter.increment.call(bad); // エラー: 'value' がない
TypeScript

「このメソッドは、こういう形のオブジェクトにバインドされる前提で書いている」
というのを、型としてはっきりさせるために thisパラメータを明示します。

3. ライブラリ的な「ユーティリティ関数」を this ベースで設計するとき

例えば、Array.prototype 風の関数を this ベースで書くとします。

function first<T>(this: T[]): T | undefined {
  return this[0];
}

const arr = [1, 2, 3];

first.call(arr); // 1
TypeScript

ここでは、

this: T[] と書くことで、「配列にバインドされる前提の関数」であることを宣言しています。

このパターンは、
「既存のオブジェクトにメソッド風の関数を後付けしたい」
「プロトタイプ拡張風のユーティリティを this ベースで書きたい」
といったときに使えます。

thisパラメータを明示しないと、
first.call({}) のような無茶な呼び出しも通ってしまうので、
「this を前提にした設計をするなら、型で縛る」という意識が大事です。


逆に「this: void」を明示すべき場面

this を絶対に使わせたくない関数

TypeScript には、こんな書き方もあります。

function log(this: void, message: string) {
  console.log(message);
}
TypeScript

this: void と書くと、

「この関数は this に依存しない」
「this をバインドして呼んではいけない」

という意味になります。

const obj = { prefix: "[X]" };

// log.call(obj, "hello"); // エラー: this は void でなければならない
log("hello"); // OK
TypeScript

ここでの重要ポイントは、

「this を使わないことを、あえて型で宣言する」 という発想です。

「この関数は純粋な関数として設計したい」
「this に依存するような使い方を禁止したい」

というときに、this: void を明示しておくと、
将来の自分や他の開発者が call / apply で変な使い方をするのを防げます。


thisパラメータとアロー関数の関係

アロー関数には thisパラメータを書けない

ここは設計上かなり重要です。

// これは OK
function f(this: { value: number }) {
  this.value++;
}

// これは NG(コンパイルエラー)
const g = (this: { value: number }) => {
  // ...
};
TypeScript

アロー関数は「自分自身の this を持たず、外側の this をキャプチャする」仕様なので、
TypeScript でも thisパラメータを宣言できません。

この事実から、設計の指針が1つ見えてきます。

this に依存する関数を書きたいなら:
普通の function / メソッド構文+thisパラメータを使う

this に依存させたくない関数を書きたいなら:
アロー関数+(必要なら)this: void を使う

「this を使うかどうか」で、関数の書き方を意識的に選ぶ
というのが、TypeScript での関数設計のコツです。


まとめ:thisパラメータの明示を自分の言葉で言うと

最後に、あなた自身の言葉でこう整理してみてください。

thisパラメータを明示するとは、

「この関数は、こういう形の this を前提に書かれている」
「あるいは、this を一切使わない(this: void)」

ということを、型として宣言すること。

特に意味があるのは、

自由関数で this に依存するとき
オブジェクトリテラルのメソッドで this の前提をはっきりさせたいとき
ユーティリティ関数を this ベースで設計するとき
this に依存させたくない関数を“純粋”として守りたいとき

コードを書くとき、
「この関数は this に依存している? 依存させたい? させたくない?」
と一度立ち止まってから、this: 型 を書くかどうかを決めてみてください。

その一呼吸で、
this は「なんとなくそこにいるやつ」から、
あなたの設計意図をきちんと表現する“型付きのコンテキスト” に変わっていきます。

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