← Back to TypeScript Menu | 🏠 Hub
// Problem: type-specific functions (code duplication)
function firstString(arr: string[]): string { return arr[0]; }
function firstNumber(arr: number[]): number { return arr[0]; }
// With any — loses type safety
function firstAny(arr: any[]): any { return arr[0]; }
const val = firstAny([1, 2, 3]);
val.toUpperCase(); // no error at compile time — runtime crash!
// With Generic — type-safe AND reusable!
function first<T>(arr: T[]): T {
return arr[0];
}
const num = first([1, 2, 3]); // T = number, returns number
const str = first(["a", "b"]); // T = string, returns string
const usr = first([{id: 1}, {id: 2}]); // T = {id:number}
num.toFixed(2); // ✅ IDE knows it's number
str.toUpperCase(); // ✅ IDE knows it's string
// Multiple generics
function pair<A, B>(first: A, second: B): [A, B] {
return [first, second];
}
const p = pair("hello", 42); // [string, number]
// Generic with constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: "Ko", email: "ko@test.com" };
getProperty(user, "name"); // ✅ string
getProperty(user, "id"); // ✅ number
getProperty(user, "phone"); // ❌ Error: 'phone' doesn't exist!
// Generic API wrapper
async function fetchData<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as T;
}
interface User { id: number; name: string; }
interface Post { id: number; title: string; }
const users = await fetchData<User[]>("/api/users"); // User[]
const post = await fetchData<Post>("/api/posts/1"); // Post
users[0].name; // ✅ auto-complete!
// Generic interface
interface ApiResponse<T> {
data: T;
status: number;
message: string;
meta?: {
total: number;
page: number;
limit: number;
};
}
// Usage
const userResponse: ApiResponse<User[]> = { data: [], status: 200, message: "OK" };
const postResponse: ApiResponse<Post> = { data: { id: 1, title: "..." }, status: 200, message: "OK" };
// Generic class
class Stack<T> {
private items: T[] = [];
push(item: T): void { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
peek(): T | undefined { return this.items[this.items.length - 1]; }
isEmpty(): boolean { return this.items.length === 0; }
get size(): number { return this.items.length; }
}
const numStack = new Stack<number>();
numStack.push(1);
numStack.push(2);
numStack.pop(); // 2, type: number
const strStack = new Stack<string>();
strStack.push("a"); // only strings allowed
| Utility | Description | Example |
|---|---|---|
| Partial<T> | All fields optional | Partial<User> |
| Required<T> | All fields required | Required<Partial<User>> |
| Readonly<T> | All fields readonly | Readonly<User> |
| Pick<T,K> | Select fields | Pick<User,'id'|'name'> |
| Omit<T,K> | Exclude fields | Omit<User,'password'> |
| Record<K,V> | Key-value map | Record<string,number> |
| Exclude<T,U> | Exclude from union | Exclude<'a'|'b'|'c','a'> |
| ReturnType<F> | Function return type | ReturnType<typeof fn> |
// Practical utility type usage
interface User { id: number; name: string; email: string; password: string; createdAt: Date; }
// Create DTO (no id, no timestamps)
type CreateUserDTO = Omit<User, 'id' | 'createdAt'>;
// Update DTO (all optional, no id)
type UpdateUserDTO = Partial<Omit<User, 'id'>>;
// Safe response (no password)
type UserResponse = Omit<User, 'password'>;
// Config (all readonly)
type UserConfig = Readonly<User>;
← TS 04 | Next: TS 06 → TypeScript + Vue →