TypeScript | 関数・クラス・ジェネリクス:ジェネリクス基礎 - 型パラメータの命名規則

TypeScript TypeScript
スポンサーリンク

ゴール:「T って何?を“意味のある名前”にできるようになる」

ジェネリクスを書き始めると、まず最初に出てくる疑問がこれです。

T って、なんでみんな T って書いてるの?
U とか K とか、あれ何?」

ここで大事なのは、

「型パラメータにも“意味のある名前”をつける」

という感覚です。

T だけが正解ではありません。
ただし、ある程度の“お作法”があるので、それを知っておくと読み書きがかなり楽になります。


一番基本の型パラメータ名:T

「Type の T」だと思っておけばOK

まず、いちばんよく見るのが T です。

function identity<T>(value: T): T {
  return value;
}
TypeScript

これはもう「Type の T」と覚えてしまって構いません。

意味としては、

「ここには“なんらかの型”が入る。
特に名前をつけるほどの文脈はないけど、
とにかく“1つの型”として扱いたい。」

くらいのニュアンスです。

例えば、配列の先頭を返す関数もそうです。

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

ここでの T は「配列の要素型」です。

このレベルなら、T で十分です。
「とにかく 1 種類の型を表す変数」として使っているだけなので、
わざわざ長い名前にする必要はありません。


2つ目以降の型パラメータ:U, V など

「T と“別の型”」を表したいとき

型パラメータが 2 つ以上になるとき、
よく出てくるのが U, V です。

function pair<T, U>(a: T, b: U): [T, U] {
  return [a, b];
}
TypeScript

ここでは、

  • T は第1引数の型
  • U は第2引数の型

という意味です。

TU に特別な意味があるわけではなく、

「T とは別の型だよ」ということを示すための記号

だと思ってください。

もし 3 つ目が必要なら V
それ以上なら TKey, TValue のように、
意味を持たせた名前にしていくことが多いです。


コレクション系でよく使う命名:TItem, TElement など

「中身の型」が分かる名前にする

配列やリスト、Box のような「何かを入れる型」を扱うときは、
T よりも少し意味のある名前にすると読みやすくなります。

class Box<TItem> {
  constructor(private value: TItem) {}

  getValue(): TItem {
    return this.value;
  }
}
TypeScript

TItem と書くことで、

「この型パラメータは“中に入っているアイテムの型”なんだな」

と一目で分かります。

同じように、TElement, TValue などもよく使われます。

function mapArray<TElement, TResult>(
  arr: TElement[],
  fn: (value: TElement) => TResult
): TResult[] {
  return arr.map(fn);
}
TypeScript

ここでは、

  • TElement は元の配列の要素型
  • TResult は変換後の要素型

という意味です。

「T だけだと分かりにくいと感じたら、
T に“意味のあるサフィックス”をつける」

というのが、実務でよくやる工夫です。


オブジェクト系でよく使う命名:TKey, TValue, TRecord など

キーと値を区別したいとき

オブジェクトを扱うジェネリクスでは、
KV、それに TKey, TValue がよく出てきます。

function getProp<TObj, TKey extends keyof TObj>(
  obj: TObj,
  key: TKey
): TObj[TKey] {
  return obj[key];
}
TypeScript

ここでは、

  • TObj は「オブジェクト全体の型」
  • TKey は「そのオブジェクトのキーの型」

という意味です。

TObjTKey のように、
「何の型なのか」が名前から分かるようにしておくと、
あとから読んだときの理解コストがかなり下がります。

例えば、マップ的な型ならこうも書けます。

type MapLike<TKey, TValue> = {
  get(key: TKey): TValue | undefined;
  set(key: TKey, value: TValue): void;
};
TypeScript

K / V だけでも通じますが、
TKey / TValue の方が、
「これはキーの型」「これは値の型」と一目で分かります。


制約付きジェネリクスでの命名:T extends 〜 の T

「何のグループの中の T なのか」を名前に乗せる

制約をつけるときも、名前に意味を持たせると読みやすくなります。

function getLength<TValue extends { length: number }>(
  value: TValue
): number {
  return value.length;
}
TypeScript

ここで TValue としておくと、

「これは“値の型”で、length を持つものなんだな」

と分かります。

もし T だけだと、

function getLength<T extends { length: number }>(value: T): number { ... }
TypeScript

となり、「T が何者か」はコードをよく読まないと分かりません。

「extends の右側を見て、“これは何の T か”を名前に反映する」

という意識を持つと、命名の質が一段上がります。


実務での現実的なルールまとめ

僕が実際に使っている感覚的ルール

厳密な“公式ルール”というより、
現場でよく使われている「いい感じの慣習」をまとめると、こんな感じです。

単純な 1 型パラメータなら T で十分

function identity<T>(value: T): T {
  return value;
}
TypeScript

「とにかく 1 つの型を表すだけ」であれば、
T でシンプルに書く方が読みやすいです。

2 つ目以降は U, V か、意味のある名前

function pair<T, U>(a: T, b: U): [T, U] { ... }
TypeScript

あるいは、意味を持たせてこうも書けます。

function pair<TLeft, TRight>(left: TLeft, right: TRight): [TLeft, TRight] { ... }
TypeScript

どちらがいいかは、
「その関数の文脈で、どれだけ意味を持たせたいか」で決めます。

コレクション系は TItem / TElement / TValue など

class Box<TItem> { ... }
function mapArray<TElement, TResult>(...) { ... }
TypeScript

「中身の型」であることが分かる名前にすると、
後から読んだときに迷いません。

オブジェクト系は TObj / TKey / TValue が鉄板

function getProp<TObj, TKey extends keyof TObj>(obj: TObj, key: TKey): TObj[TKey] { ... }
TypeScript

T, K だけよりも、
TObj, TKey の方が圧倒的に読みやすいです。


失敗しがちなパターンと、どう直すか

なんでもかんでも T, U, V にしてしまう

例えば、こんなコード。

function doSomething<T, U, V>(a: T, b: U, c: V): void {
  // ...
}
TypeScript

中身を見ないと、
T/U/V が何を表しているのかまったく分かりません。

これを少しだけマシにすると、こうなります。

function doSomething<TUser, TConfig, TResult>(
  user: TUser,
  config: TConfig,
  initial: TResult
): TResult {
  // ...
}
TypeScript

「完璧な名前」を目指す必要はありませんが、

「少なくとも、何の種類の型なのか」は名前から分かるようにする

という意識を持つと、
自分も他人もだいぶ楽になります。


まとめ:型パラメータの命名規則を自分の言葉で整理すると

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

型パラメータは、ただの記号ではなく「型の変数の名前」。
だからこそ、値の変数と同じように「意味のある名前」をつけると読みやすくなる。

シンプルな 1 つの型なら T で十分。
2 つ目以降は U, V か、TKey, TValue のように意味を持たせる。
コレクションなら TItem / TElement
オブジェクトなら TObj / TKey / TValue が定番。

今書いているジェネリック関数やクラスの型パラメータを、
一度全部眺めてみてください。

「この T、もう少し意味のある名前にできるな」と思ったところを、
1 箇所だけでもいいのでリネームしてみる。

その小さな修正を繰り返すうちに、
「型パラメータの命名センス」は、確実に育っていきます。

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