🔧 TypeScript — Creating Types from Types: Build Reusable and Dynamic Type Structures
🧲 Introduction – What Does It Mean to Create Types from Types in TypeScript?
One of TypeScript’s most powerful features is its ability to create new types from existing ones. This concept—known as type transformation—allows developers to build scalable, DRY (Don’t Repeat Yourself), and highly maintainable type systems. Instead of manually rewriting type structures, you can dynamically generate new types using built-in tools such as utility types, mapped types, conditional types, and more.
🎯 In this guide, you’ll learn:
- How to create new types based on existing ones
- The role of utility types like
Pick,Omit, andPartial - How to use
keyof,typeof, and indexed access types - Real-world use cases and best practices
🔁 Why Create Types from Types?
Creating new types from existing ones enables:
- ✅ Code reusability
- ✅ Cleaner and consistent models
- ✅ Less duplication
- ✅ Safer refactoring
- ✅ Easier API integration and validation
This feature is especially useful in large applications with shared interfaces, models, and schemas.
🔧 1. Using typeof to Derive Types from Variables
You can use typeof to infer the type of a constant or variable and reuse it elsewhere.
✅ Example:
const user = {
name: "Alice",
age: 30,
email: "alice@example.com"
};
type User = typeof user;
📌 Now, User has the exact same structure as the user object.
🔠 2. Using keyof to Get Keys of a Type
The keyof operator returns a union of all keys in a type.
type UserKeys = keyof User; // "name" | "age" | "email"
✅ Useful for creating dynamic property constraints or building helper types.
🪄 3. Using Indexed Access Types
You can extract the type of a specific property in another type.
type UserEmail = User["email"]; // string
📌 This is helpful when you need a specific property type from a complex object.
🧱 4. Creating Types with Pick
Use Pick<T, K> to create a type by selecting specific properties from an existing type.
type UserSummary = Pick<User, "name" | "email">;
✅ This is great for preview cards, summaries, or public user views.
✂️ 5. Creating Types with Omit
Use Omit<T, K> to exclude properties from a type.
type UserWithoutEmail = Omit<User, "email">;
🧠 Helps when you need to remove sensitive or unnecessary fields.
🧩 6. Making Properties Optional with Partial
Use Partial<T> to make all properties in a type optional.
type UpdateUser = Partial<User>;
📌 Ideal for update forms or patch requests where not all fields are required.
🔐 7. Making All Properties Read-Only with Readonly
Use Readonly<T> to ensure the properties cannot be modified.
type ReadonlyUser = Readonly<User>;
✅ Perfect for immutable data structures or configuration objects.
🧠 8. Combining Types with Intersection (&) and Union (|)
✅ Intersection:
type Address = {
street: string;
city: string;
};
type UserWithAddress = User & Address;
✅ Combines properties from both types.
✅ Union:
type Response = User | null;
✅ Useful when a variable can hold multiple valid types.
🧪 9. Creating Types from Function Returns with ReturnType
function getUser() {
return {
id: 1,
name: "Alice"
};
}
type FetchedUser = ReturnType<typeof getUser>; // { id: number; name: string }
📌 Useful when you want your types to match function outputs exactly.
🔁 10. Dynamic Type Creation with Mapped Types
type Flags = {
darkMode: boolean;
sidebar: boolean;
};
type FlagActions = {
[K in keyof Flags]: () => void;
};
📌 Mapped types let you dynamically create structures based on existing keys.
📚 Real-World Use Cases
- 🔄 API request/response models
- 🔐 Excluding sensitive fields from user objects
- 🎛️ Form builders with dynamic validation
- 🧱 Reusing object models in components or services
- 🧩 Creating config schemas and validators
⚠️ Common Mistakes & How to Avoid Them
| ❌ Mistake | ✅ Solution |
|---|---|
| Manually rewriting similar types | Use Pick, Omit, or Partial for consistency |
| Forgetting to update derived types | Always derive new types using tools like typeof or ReturnType |
| Ignoring union and intersection | Use them to create flexible and extendable types |
| Over-nesting utility types | Break complex types into aliases for readability |
💡 Best Practices
- ✅ Use
typeofandkeyofto reference real structures - ✅ Chain utility types thoughtfully (e.g.,
Partial<Pick<T, K>>) - ✅ Use aliases for long or repeated type chains
- ✅ Combine interfaces and utility types for maximum flexibility
- ✅ Document custom types for team clarity
📌 Summary – Recap & Takeaways
Creating types from types is a core principle of TypeScript’s type system flexibility. It helps you write DRY, maintainable, and error-resistant code by enabling type reuse and transformation without redefining structures.
🔍 Key Takeaways:
- Use
typeof,keyof,Pick,Omit, and other utilities to derive types - Enables safe reuse of existing type definitions
- Great for APIs, forms, configurations, and component props
- Reduces redundancy and improves scalability of your codebase
⚙️ Practical relevance: This is heavily used in enterprise applications, full-stack TypeScript projects, form management, GraphQL schemas, and RESTful API services.
❓ FAQs – Creating Types from Types in TypeScript
❓ Can I create a type from an object instance?
✅ Yes, using typeof on the object.
❓ What’s the difference between Pick and Omit?
📌 Pick includes specific keys; Omit removes specific keys.
❓ Can I extract a property type from another type?
✅ Yes, with T["propertyName"].
❓ Is it better to write new types or reuse existing ones?
✅ Always prefer creating new types from existing ones when possible.
❓ Can I combine multiple type transformations?
✅ Yes. Example: Readonly<Partial<Pick<T, K>>>
Share Now :
