Built-in types beat custom aliases
TypeScript ships with utility types that solve 80% of shape-transform problems. Reaching for interface Foo extends Bar every time often creates duplication you don't need.
Utility types are defined in TypeScript's standard library — no imports required.
Pick and Omit
When you only need a subset of fields:
interface Post {
title: string
slug: string
date: string
content: string
featured: boolean
}
type PostMeta = Pick<Post, 'title' | 'slug' | 'date'>
type PostInput = Omit<Post, 'slug' | 'date'>Pick keeps listed keys. Omit removes them — useful for create forms where the server generates slug and date.
Partial and Required
Update endpoints often accept optional fields:
type PostUpdate = Partial<Pick<Post, 'title' | 'description' | 'featured'>>
function updatePost(slug: string, data: PostUpdate) {
// data.title might be undefined — that's intentional
}Flip it with Required<T> when every key must be present after validation.
Record for dictionaries
Maps and lookup tables read cleaner with Record:
type TagColor = 'react' | 'typescript' | 'css' | 'default'
const tagStyles: Record<TagColor, string> = {
react: 'tag-pill--react',
typescript: 'tag-pill--typescript',
css: 'tag-pill--css',
default: 'tag-pill--default'
}ReturnType and Parameters
Extract types from functions instead of duplicating them:
async function fetchPosts() {
return [{ title: 'Hello', slug: 'hello' }] as const
}
type Posts = Awaited<ReturnType<typeof fetchPosts>>
type FetchPostsArgs = Parameters<typeof fetchPosts>This keeps API wrappers and mocks in sync when the function signature changes.
interface PostsResponse {\n posts: { title: string; slug: string }[]\n}\n\nasync function getPosts(): Promise<PostsResponse>async function getPosts() {\n return { posts: await db.post.findMany() }\n}\n\ntype PostsResponse = Awaited<ReturnType<typeof getPosts>>Exclude and Extract
Narrow unions when handling status codes or theme modes:
type Status = 'draft' | 'published' | 'archived'
type LiveStatus = Exclude<Status, 'draft'>
type AlertType = 'info' | 'warning' | 'error' | 'tip'
type DangerAlert = Extract<AlertType, 'warning' | 'error'>NonNullable
Filter null from inferred types:
function getFeaturedPost(posts: PostMeta[]) {
return posts.find((p) => p.featured) ?? null
}
type Featured = NonNullable<ReturnType<typeof getFeaturedPost>>Conclusion
Utility types are composable — Partial<Pick<Post, 'title'>> is valid and often exactly what an PATCH handler needs. Start with Pick, Omit, and Partial; reach for ReturnType when bridging functions and components.
If a utility type gets hard to read, give it a named alias. Readability still wins over clever one-liners.
