TypeScript
JavaScript
Web Development
Programming
Frontend
Ask anything about this article
Hi! I've read this article.
What would you like to know?
@farhan
If you're still using TypeScript like "JavaScript with types," you're missing out on its most powerful features. Let's level up.
The most useful pattern in TypeScript:
typescripttype Result<T> =
| { success: true; data: T }
| { success: false; error: string }
function handleResult(result: Result<User>) {
if (result.success) {
// TypeScript knows result.data exists here
console.log(result.data.name)
} else {
// TypeScript knows result.error exists here
console.log(result.error)
}
}
// Real-world: API response handling
type APIResponse =
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; message: string; code: number }
Build types from string patterns:
typescripttype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type APIRoute = `/api/${string}`
type Endpoint = `${HTTPMethod} ${APIRoute}`
// "GET /api/users" ✅
// "PATCH /api/users" ❌
// Event handler types
type EventName = 'click' | 'hover' | 'focus'
type HandlerName = `on${Capitalize<EventName>}`
// "onClick" | "onHover" | "onFocus"
Prevent mixing up values of the same primitive type:
typescripttype Brand<T, B> = T & { __brand: B }
type USD = Brand<number, 'USD'>
type EUR = Brand<number, 'EUR'>
type UserId = Brand<string, 'UserId'>
type OrderId = Brand<string, 'OrderId'>
function processPayment(amount: USD, userId: UserId) {
// ...
}
const amount = 100 as USD
const userId = "user_123" as UserId
const orderId = "order_456" as OrderId
processPayment(amount, userId) // ✅
processPayment(amount, orderId) // ❌ Type error!
typescript// Extract return type of async functions
type AsyncReturnType<T> = T extends (...args: any[]) => Promise<infer R> ? R : never
async function fetchUser() {
return { id: 1, name: "Farhan" }
}
type User = AsyncReturnType<typeof fetchUser>
// { id: number; name: string }
// Extract array element type
type ElementOf<T> = T extends (infer E)[] ? E : never
type Item = ElementOf<string[]> // string
typescriptclass QueryBuilder<T extends Record<string, any>> {
private query: Partial<T> = {}
where<K extends keyof T>(key: K, value: T[K]): this {
this.query[key] = value
return this
}
build(): Partial<T> {
return { ...this.query }
}
}
interface UserFilter {
name: string
age: number
active: boolean
}
const query = new QueryBuilder<UserFilter>()
.where('name', 'Farhan') // ✅ string
.where('age', 25) // ✅ number
.where('age', 'twenty-five') // ❌ Type error!
.build()
typescript// DeepPartial — make all nested properties optional
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
// StrictOmit — Omit that ensures key exists
type StrictOmit<T, K extends keyof T> = Omit<T, K>
// RequireAtLeastOne — at least one property required
type RequireAtLeastOne<T> = {
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>
}[keyof T]
typescripttype EventMap = {
'user:login': { userId: string; timestamp: number }
'user:logout': { userId: string }
'order:created': { orderId: string; total: number }
}
class TypedEmitter<T extends Record<string, any>> {
private handlers = new Map<string, Function[]>()
on<K extends keyof T>(event: K, handler: (data: T[K]) => void): void {
const existing = this.handlers.get(event as string) || []
existing.push(handler)
this.handlers.set(event as string, existing)
}
emit<K extends keyof T>(event: K, data: T[K]): void {
this.handlers.get(event as string)?.forEach(fn => fn(data))
}
}
const emitter = new TypedEmitter<EventMap>()
emitter.on('user:login', (data) => {
console.log(data.userId) // ✅ fully typed
console.log(data.total) // ❌ doesn't exist on login event
})
Advanced TypeScript isn't about complexity — it's about catching bugs before they reach production. Master these patterns and your code quality will skyrocket.