Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. TypeScript Dictionary
  3. Generics Basics <T>

Generics Basics <T>

Since: TypeScript 1.0(2014)

A feature that accepts types as arguments, allowing you to reuse the same logic for a variety of types. You can apply generics to functions, interfaces, and classes to write flexible, reusable code while keeping type checking active.

Syntax

function functionName<T>(arg: T): T {
	return arg;
}

// Arrow function syntax
const functionName = <T>(arg: T): T => arg;

// Explicitly specifying the type argument when calling
functionName<string>("value");

// Type argument inference: you can omit the type if it can be determined from the argument.
functionName("value"); // T is inferred as string.

Syntax reference

SyntaxDescription
<T>Declares a type parameter. Write it enclosed in <> after the function or type name. By convention, T is commonly used, but any name is allowed.
function f<T>(arg: T): TThe basic form of a generic function. Links the parameter type and return type using the same type parameter.
Type argument inferenceAutomatically determines the type parameter from the argument at the call site. Explicit specification is often unnecessary.
Multiple type parametersYou can define multiple type parameters, such as <T, U>.

Sample code

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

// Calling with an explicit type argument.
const str = identity<string>("hello"); // type is string
const num = identity<number>(42); // type is number

// The type can be omitted thanks to type inference.
const autoStr = identity("TypeScript"); // T is inferred as string.
const autoNum = identity(100); // T is inferred as number.
console.log(autoStr); // Outputs "TypeScript".

function first<T>(arr: T[]): T | undefined {
	return arr[0];
}
console.log(first([1, 2, 3])); // Outputs 1.
console.log(first(["a", "b", "c"])); // Outputs "a".
console.log(first([])); // Outputs undefined.

// Using multiple type parameters.
function pair<T, U>(first: T, second: U): [T, U] {
	return [first, second];
}
const result = pair("item_x", 250); // type is [string, number].
console.log(result); // Outputs ["item_x", 250].

// A generic function that concatenates two arrays.
function merge<T>(arr1: T[], arr2: T[]): T[] {
	return [...arr1, ...arr2];
}
const merged = merge([1, 2], [3, 4]); // type is number[].
console.log(merged); // Outputs [1, 2, 3, 4].

// A stack (last-in, first-out) implementation using generics.
function createStack<T>() {
	const items: T[] = [];
	return {
		push: (item: T) => items.push(item),
		pop: (): T | undefined => items.pop(),
		peek: (): T | undefined => items[items.length - 1],
		size: () => items.length,
	};
}
const stack = createStack<number>();
stack.push(1);
stack.push(2);
console.log(stack.pop()); // Outputs 2.

Running the above produces the following output:

npx ts-node sample_ts_generics_basic.ts
TypeScript
1
a
undefined
[ 'item_x', 250 ]
2

Constraints (extends) integration

Adding extends to a type parameter restricts which types are accepted. Constraints let you inform the compiler that a specific property exists on the type parameter.

function getLength<T extends { length: number }>(value: T): number {
	return value.length;
}
console.log(getLength("Hello, World!")); // 13
console.log(getLength([1, 2, 3])); // 3
// console.log(getLength(42)); // Compile error (number has no length)

// Combined with keyof: safely retrieve an object property.
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
	return obj[key];
}
const product = { name: "item_x", price: 250, category: "electronics" };
console.log(getProperty(product, "name")); // item_x
console.log(getProperty(product, "price")); // 250
// console.log(getProperty(product, "color")); // Compile error

// Default type parameters (TypeScript 2.3+)
interface Box<T = string> {
	value: T;
}
const strBox: Box = { value: "config_data" }; // T defaults to string
const numBox: Box<number> = { value: 500 }; // T is explicitly number
console.log(strBox.value, numBox.value); // config_data 500

Running the above produces the following output:

npx ts-node sample_generics_extends.ts
13
3
item_x
250
config_data 500

Practical patterns

Common generic patterns used in real-world TypeScript projects.

interface ApiResponse<T> {
	data: T | null;
	error: string | null;
	status: number;
}

interface User {
	id: number;
	name: string;
}

function createSuccess<T>(data: T): ApiResponse<T> {
	return { data, error: null, status: 200 };
}

function createError<T>(message: string, status: number): ApiResponse<T> {
	return { data: null, error: message, status };
}

const userResponse = createSuccess<User>({ id: 1, name: "member_1" });
const errResponse = createError<User>("Not Found", 404);
console.log(userResponse.data?.name); // member_1
console.log(errResponse.error); // Not Found

// Pattern 2: Type-safe event emitter
type EventMap = {
	login:  { userId: number; username: string };
	logout: { userId: number };
};

function emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {
	console.log(`Event: ${event}`, payload);
}

emit("login", { userId: 1, username: "user_1" });
// emit("login", { userId: 1 }); // Compile error (username is missing)

Running the above produces the following output:

npx ts-node sample_generics_patterns.ts
member_1
Not Found
Event: login { userId: 1, username: 'user_1' }

Common Mistakes

Common mistake 1: using any

Using any disables type checking, and the type-tracking that generics provide no longer functions.

function badIdentity(value: any): any { return value; }

Fix: use generics to define a general-purpose function while keeping type checking active.

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

Common mistake 2: unconstrained property access

Accessing a property on a type parameter without a constraint causes a compile error. TypeScript does not know that T has a name property.

function getNameBad<T>(obj: T): string { return obj.name; }

Fix: add a constraint with extends.

function getNameGood<T extends { name: string }>(obj: T): string {
	return obj.name;
}

Common mistake 3: omitting type arguments

Omitting the type argument causes T to become unknown, which leads to type errors.

class Repository<T> {
	private items: T[] = [];
	add(item: T): void { this.items.push(item); }
	getAll(): T[] { return this.items; }
}
const repo = new Repository(); // T becomes unknown, leading to type errors

Fix: specify the type argument explicitly.

interface User { id: number; name: string; }
class Repository<T> {
	private items: T[] = [];
	add(item: T): void { this.items.push(item); }
	getAll(): T[] { return this.items; }
}
const repo = new Repository<User>(); // Specify the type argument explicitly
repo.add({ id: 2, name: "member_2" });
console.log(repo.getAll()[0].name); // member_2

Running the above produces the following output:

npx ts-node sample_generics_mistakes.ts
member_2

Overview

Generics let you treat types like variables. By using a type parameter (<T>), you can define functions and types whose actual type is determined at the call site. This lets you write a single function that works with both string arrays and number arrays.

By convention, type parameter names like T (Type), K (Key), V (Value), and E (Element) are commonly used, but you can choose any descriptive name you prefer. When you need multiple type parameters, separate them with commas, as in <T, U>.

TypeScript can usually infer the type parameter from the argument, so you can write identity("hello") instead of identity<string>("hello"). Specify the type argument explicitly only when inference does not work as expected or when you want to make your intent clear. To add constraints to a type parameter, see Generic constraints: extends.

If you find any errors or copyright issues, please .