Type Parameters (Generics)
- Selector:
typeParameter - Required:
PascalCase - Custom: Must match one of the allowed patterns for generic type parameters
Why This Rule
Generic type parameters in TypeScript use specific conventions that differ from regular types:
- Single uppercase letters (T, U, V, K): Most common for general-purpose type parameters
- T/K/V prefix + descriptive name: Combines clarity with convention (TData, KKey, VValue)
- Numeric subscripts (T0, T1, T2, T3): For sequences of related types
These conventions are:
- Widely recognized: Established patterns across TypeScript, Java, C#, and other languages
- Self-documenting: Instantly recognizable as generic placeholders
- Concise: Short names reduce noise in generic-heavy code
Allowed Patterns
Single Letters
The four standard single-letter generics follow clear conventions:
T: Most common for a single generic type (Type)U: Second generic type (alphabetical progression after T)V: Third generic type (alphabetical progression after U)K: Standard for keys in key-value pairs (e.g.,Map<K, V>,Record<K, V>)
Descriptive with Prefix
For better clarity, prefix descriptive names with:
Tprefix:TData,TItem,TElement,TErrorKprefix:KKey(for keys)Vprefix:VValue(for values)
Numeric Subscripts
Use numeric subscripts for sequences of related types:
T0, T1 T2, T3, ...: Useful for tuples or multiple similar generic parametersK1, K2: For multiple key typesV1, V2: For multiple value types
Note: The number must start with 1-9 (not 0), and can be followed by additional digits.
References
- Generic Type Parameters in TypeScript - Comprehensive guide to generic naming
- Tidy TypeScript: Name your generics - Best practices for generic names
- How to Name your Types - Matt Pocock - Expert advice on type naming
- TypeScript Generic Naming Conventions - Community discussion on conventions
✅ Good
ts
// ✅ Single letter generics - T is most common
export function identity<T>(arg: T): T {
return arg;
}
// ✅ Multiple generic types - T and U in alphabetical progression
export function map<T, U>(value: T, transform: (v: T) => U): U {
return transform(value);
}
// ✅ Key-Value pairs - K and V are standard for dictionaries
export interface Dictionary<K, V> {
get(key: K): V | undefined;
set(key: K, value: V): void;
}
// ✅ Descriptive generic with T prefix - combines clarity with convention
export function mapArray<TItem, TResult>(
items: TItem[],
transform: (item: TItem) => TResult,
): TResult[] {
return items.map(transform);
}
// ✅ Descriptive generic with T prefix - makes key/value types explicit
export type Cache<TKey, TValue> = Map<TKey, TValue>;
// ✅ Numeric subscript convention - useful for tuples
export type Tuple<T0, T1, T2, T3, T999> = [T0, T1, T2, T3, T999];
// ✅ Complex example combining all conventions
export class GenericContainer<T, TData, K1, K2> {
private value: T;
private data: TData;
private keys: [K1, K2];
constructor(value: T, data: TData, keys: [K1, K2]) {
this.value = value;
this.data = data;
this.keys = keys;
}
}❌ Bad
ts
// ❌ Descriptive name without prefix - should use T or TType
function bad1<Type>(arg: Type): Type {
return arg;
}
// ❌ Lowercase t prefix - generic type parameters must be uppercase
function bad2<tItem>(items: tItem[]): tItem[] {
return items;
}
// ❌ No T prefix for descriptive names - should use TData
function bad3<Data>(data: Data): Data {
return data;
}
// ❌ Redundant Type suffix - TItem is sufficient
function bad4<TItemType>(item: TItemType): TItemType {
return item;
}
// ❌ Redundant prefix on K and V - should be TKey and TValue, not KKey and VVal
interface BadDict<KKey, VVal> {
get(key: KKey): VVal;
}
// ❌ Non-standard single letters (lowercase) - must be uppercase
function bad5<a, b>(x: a, y: b): [a, b] {
return [x, y];
}
// ❌ Snake case - should use PascalCase like TItem
function bad6<T_Item>(items: T_Item[]): T_Item[] {
return items;
}
// ❌ camelCase - should use PascalCase like TData
function bad7<tData>(data: tData): tData {
return data;
}