6 min read
TypeScript Tips for Better Code
Practical TypeScript techniques that will make your code more robust and maintainable. From utility types to advanced patterns.
TypeScriptJavaScriptDevelopment
# TypeScript Tips for Better Code
TypeScript has become essential for building robust JavaScript applications. Here are practical tips and techniques to level up your TypeScript skills.
## Utility Types
TypeScript provides powerful utility types that can make your code more maintainable:
### Pick and Omit
``
typescript
interface User {
id: string
name: string
email: string
password: string
createdAt: Date
}
// Pick specific properties
type PublicUser = Pick
// Omit sensitive properties
type SafeUser = Omit
`
### Partial and Required
`typescript
// Make all properties optional
type UserUpdate = Partial
// Make all properties required
type CompleteUser = Required
`
## Advanced Type Patterns
### Conditional Types
`typescript
type ApiResponse = T extends string
? { message: T }
: { data: T }
type StringResponse = ApiResponse // { message: string }
type DataResponse = ApiResponse // { data: User[] }
`
### Mapped Types
`typescript
type Readonly = {
readonly [P in keyof T]: T[P]
}
type Optional = {
[P in keyof T]?: T[P]
}
`
## Generic Constraints
Use constraints to limit generic types:
`typescript
interface Identifiable {
id: string
}
function updateEntity(
entity: T,
updates: Partial
): T {
return { ...entity, ...updates }
}
`
## Type Guards
Create custom type guards for runtime type checking:
`typescript
function isString(value: unknown): value is string {
return typeof value === 'string'
}
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
'email' in obj
)
}
// Usage
function processData(data: unknown) {
if (isUser(data)) {
// TypeScript knows data is User here
console.log(data.name)
}
}
`
## Discriminated Unions
Use discriminated unions for type-safe state management:
`typescript
type LoadingState = {
status: 'loading'
}
type SuccessState = {
status: 'success'
data: User[]
}
type ErrorState = {
status: 'error'
error: string
}
type AsyncState = LoadingState | SuccessState | ErrorState
function handleState(state: AsyncState) {
switch (state.status) {
case 'loading':
return
case 'success':
return
case 'error':
return
}
}
`
## Template Literal Types
Create precise string types:
`typescript
type EventName = 'click' | 'focus' | 'blur'
type EventHandler = on${Capitalize
type ClickHandler = EventHandler<'click'> // 'onClick'
type FocusHandler = EventHandler<'focus'> // 'onFocus'
`
## Strict Configuration
Use strict TypeScript configuration for better type safety:
`json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
`
## Error Handling Patterns
### Result Type Pattern
`typescript
type Result =
| { success: true; data: T }
| { success: false; error: E }
async function fetchUser(id: string): Promise> {
try {
const user = await api.getUser(id)
return { success: true, data: user }
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error('Unknown error')
}
}
}
// Usage
const result = await fetchUser('123')
if (result.success) {
console.log(result.data.name) // Type-safe access
} else {
console.error(result.error.message)
}
`
## Performance Tips
### Use const assertions
`typescript
// Instead of string[]
const colors = ['red', 'green', 'blue'] as const
// Type is readonly ['red', 'green', 'blue']
// Instead of object with string values
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
} as const
`
### Prefer interfaces over types for objects
`typescript
// ✅ Better for objects
interface User {
id: string
name: string
}
// ✅ Better for unions and computed types
type Status = 'loading' | 'success' | 'error'
``## Conclusion
These TypeScript patterns and techniques will help you write more robust, maintainable code. Start incorporating them into your projects gradually, and you'll see immediate improvements in code quality and developer experience.
Remember: TypeScript is not just about adding types—it's about leveraging the type system to catch errors early and make your code more self-documenting.