TypeScript
Public
Advanced Types (Generics & Utility Types)
Updated Mar 3, 2026
Shared by Liubomyr
TypeScript Advanced Types
Generics
// Generic function
function identity<T>(arg: T): T {
return arg;
}
const num = identity<number>(42); // Explicit
const str = identity('hello'); // Inferred
// Generic with constraints
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength('hello'); // ✅ string has length
logLength([1, 2, 3]); // ✅ array has length
// logLength(42); // ❌ Error - number doesn't have length
// Generic interface
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
const userResponse: ApiResponse<User> = {
data: { id: 1, name: 'John' },
status: 200,
message: 'Success'
};
const postsResponse: ApiResponse<Post[]> = {
data: [{ id: 1, title: 'Post 1' }],
status: 200,
message: 'Success'
};
// Generic class
class DataStore<T> {
private data: T[] = [];
add(item: T): void {
this.data.push(item);
}
get(index: number): T | undefined {
return this.data[index];
}
getAll(): T[] {
return this.data;
}
find(predicate: (item: T) => boolean): T | undefined {
return this.data.find(predicate);
}
}
const numberStore = new DataStore<number>();
numberStore.add(1);
numberStore.add(2);
const userStore = new DataStore<User>();
userStore.add({ id: 1, name: 'John' });Utility Types
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial<T> - all properties optional
type PartialUser = Partial<User>;
const update: PartialUser = { name: 'Jane' }; // ✅ Only name
// Required<T> - all properties required
type RequiredUser = Required<User>;
// Readonly<T> - all properties readonly
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = { id: 1, name: 'John', email: 'john@example.com', age: 30 };
// user.name = 'Jane'; // ❌ Error
// Pick<T, K> - pick specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
const preview: UserPreview = { id: 1, name: 'John' };
// Omit<T, K> - omit specific properties
type UserWithoutEmail = Omit<User, 'email'>;
// Record<K, T> - object with keys K and values T
type Roles = 'admin' | 'user' | 'guest';
type Permissions = Record<Roles, string[]>;
const permissions: Permissions = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read']
};
// Exclude<T, U> - exclude types
type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
// Extract<T, U> - extract types
type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // 'a' | 'b'
// NonNullable<T> - remove null and undefined
type T3 = NonNullable<string | null | undefined>; // string
// ReturnType<T> - function return type
function getUser() {
return { id: 1, name: 'John' };
}
type User = ReturnType<typeof getUser>; // { id: number; name: string }
// Parameters<T> - function parameter types as tuple
type Params = Parameters<typeof getUser>; // []
function add(a: number, b: number): number {
return a + b;
}
type AddParams = Parameters<typeof add>; // [number, number]Mapped Types
// Basic mapped type
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Optional<T> = {
[P in keyof T]?: T[P];
};
// Mapped type with modification
type Mutable<T> = {
-readonly [P in keyof T]: T[P]; // Remove readonly
};
type Concrete<T> = {
[P in keyof T]-?: T[P]; // Remove optional
};
// Transform property types
type Stringify<T> = {
[P in keyof T]: string;
};
interface Numbers {
a: number;
b: number;
}
type Strings = Stringify<Numbers>; // { a: string; b: string }