ゴール:「メソッドの型だけ見て“何をするか”がだいたい分かるように書ける」
クラスのメソッドって、書こうと思えば何でも書けます。
だからこそ「型をどう付けるか」で、読みやすさと安全性が大きく変わります。
ここでは、
- メソッドの基本的な型指定
thisを意識した設計- 戻り値の型の決め方
- コールバックやジェネリクスを使うメソッドの型
を、順番にかみ砕いていきます。
メソッドの基本的な型指定
まずは「普通の関数」と同じだと思っていい
クラスのメソッドは、基本的には「クラスの中に書く関数」です。
なので、型の付け方も関数と同じです。
class Calculator {
add(a: number, b: number): number {
return a + b;
}
log(message: string): void {
console.log("[LOG]", message);
}
}
const c = new Calculator();
const result = c.add(1, 2); // result: number
c.log("計算しました");
TypeScriptここで大事なのは、
- 引数に型を付ける
- 戻り値にも型を付ける
この2つを「必ず書く」習慣をつけることです。
戻り値の型を省略しても TypeScript は推論してくれますが、
「このメソッドは何を返す前提なのか」を明示したほうが、
読む人にも未来の自分にも親切です。
戻り値の型を“意識して”決める
例えば、ユーザーを更新するメソッドを考えます。
class User {
constructor(
public name: string
) {}
rename(newName: string): void {
this.name = newName;
}
}
TypeScriptvoid にしたのは、
- 「このメソッドは“状態を変えるだけ”で、値は返さない」
という設計の意思表示です。
一方で、メソッドチェーンをしたいなら、
自分自身を返す設計もできます。
class User {
constructor(
public name: string
) {}
rename(newName: string): this {
this.name = newName;
return this;
}
}
const u = new User("Taro").rename("Jiro").rename("Saburo");
TypeScriptここでのポイントは、
「このメソッドを呼んだあと、呼び出し側は何を受け取れると嬉しいか?」
を考えて、戻り値の型を決めることです。
this とメソッド:インスタンスの状態をどう扱うか
this は「そのインスタンス自身」
クラスのメソッドの中で this を使うと、
そのインスタンスのプロパティにアクセスできます。
class User {
constructor(
public name: string
) {}
greet(): void {
console.log(`こんにちは、${this.name}です`);
}
}
const u = new User("Taro");
u.greet(); // こんにちは、Taroです
TypeScriptここで this.name は、
「このメソッドを呼んでいるインスタンスの name」です。
メソッドの型を考えるときは、
- 引数:外から渡される情報
this:インスタンスがすでに持っている情報- 戻り値:呼び出し側に返す情報
という3つの視点で整理すると、設計しやすくなります。
this を戻り値に使うパターン
さきほど少し出した「メソッドチェーン」のパターンは、
戻り値の型に this を使うのがポイントです。
class Builder {
private text = "";
append(str: string): this {
this.text += str;
return this;
}
build(): string {
return this.text;
}
}
const b = new Builder();
const result = b.append("Hello, ").append("World").build();
TypeScriptappend の戻り値を this にすることで、
appendを何度もつなげて呼べる- 最後に
build()で結果を取り出せる
というインターフェースになります。
「このメソッドは“自分自身を返すと便利そうか?”」
という視点を持っておくと、this を戻り値に使うタイミングが見えてきます。
メソッドでコールバックを受け取るときの型
コールバックの型を“その場で書かない”
例えば、「配列の各要素に対して処理をする」メソッドをクラスで持つとします。
class NumberList {
constructor(
public values: number[]
) {}
forEach(callback: (value: number, index: number) => void): void {
this.values.forEach(callback);
}
}
TypeScriptこれでも動きますが、(value: number, index: number) => void が少し読みにくいですよね。
こういうときは、コールバックの型に名前をつけてあげると読みやすくなります。
type NumberCallback = (value: number, index: number) => void;
class NumberList {
constructor(
public values: number[]
) {}
forEach(callback: NumberCallback): void {
this.values.forEach(callback);
}
}
TypeScriptメソッドの型を読むときに、
- 引数:
callback: NumberCallback - 戻り値:
void
と、一瞬で理解できます。
「メソッドの引数に関数を渡す」場面では、
その関数の型を型エイリアスに切り出す癖をつけると、
クラスの宣言がかなりスッキリします。
ジェネリクス+コールバックのメソッド
もう少しだけ進んだ例も見ておきます。
class List<T> {
constructor(
public values: T[]
) {}
map<U>(fn: (value: T, index: number) => U): U[] {
return this.values.map(fn);
}
}
const list = new List([1, 2, 3]);
const doubled = list.map(n => n * 2); // number[]
const asString = list.map(n => `#${n}`); // string[]
TypeScriptここでのポイントは、
- クラス自体がジェネリクス
Tを持っている mapメソッドはさらにUという型パラメータを持つfnの型は(value: T, index: number) => U- 戻り値は
U[]
という関係になっていることです。
「メソッドの型にジェネリクスを使う」ときは、
- メソッドが「どんな型を受け取って」
- 「どんな型に変換して」
- 「何を返すのか」
を、型パラメータで素直に表現してあげると、
読みやすく・使いやすいメソッドになります。
メソッドの戻り値設計:void / 値 / this / Promise
void にするのは「副作用だけ」のとき
例えば、ログを出すだけのメソッド。
class Logger {
log(message: string): void {
console.log(message);
}
}
TypeScriptこれは「呼び出し側に返すべき値は特にない」ので、void が自然です。
ただし、「本当に何も返さない設計でいいのか?」は一度考えてみてください。
例えば、成功/失敗を返したいなら、
戻り値を変える選択肢もあります。
class Logger {
log(message: string): boolean {
try {
console.log(message);
return true;
} catch {
return false;
}
}
}
TypeScript値を返すメソッド:計算・変換・取得
「何かを計算して返す」「状態を元に値を返す」メソッドは、
積極的に戻り値の型を付けます。
class Rectangle {
constructor(
public width: number,
public height: number
) {}
area(): number {
return this.width * this.height;
}
isSquare(): boolean {
return this.width === this.height;
}
}
TypeScriptここでは、
areaはnumberisSquareはboolean
という「メソッドの性格」が、型からも伝わります。
非同期メソッド:Promise<…> を戻り値にする
API 呼び出しなど、非同期処理をするメソッドは async を付けます。
class UserService {
async fetchUser(id: number): Promise<User> {
const res = await fetch(`/users/${id}`);
const data = await res.json();
return data as User;
}
}
TypeScriptasync を付けると、戻り値は必ず Promise<…> になります。
ここで大事なのは、
- 「このメソッドは“結果をすぐ返す”のか、“あとで返す(Promise)”のか」
- 呼び出し側は
awaitする前提なのか
を、戻り値の型でハッキリさせることです。
まとめ:メソッドの型指定を自分の言葉で整理すると
最後に、あなた自身の言葉でこう整理してみてください。
メソッドの型を設計するときは、
- 引数の型と戻り値の型を必ず書く(推論に甘えすぎない)
thisが「インスタンスの状態」を表すことを意識する- 戻り値は
void/ 値 /this/Promise<…>のどれにするかを意図して選ぶ - コールバックを受け取るときは、その関数型に名前をつけてあげる
- ジェネリクスを使うときは「何を何に変換するメソッドか」を型パラメータで素直に表す
今書いているクラスからメソッドを1つ選んで、
「このメソッド、本当は何を返すべき?」
「引数と戻り値の型だけ見て、役割が伝わる?」
と問い直してみてください。
メソッドの型は、
“そのクラスの API の顔”です。
そこを丁寧に設計していくと、クラス全体の見通しが一気によくなります。
