In TypeScript, Mapped Types and Conditional Types are two powerful type system features that can be used to create new types based on existing ones. Combining them allows for highly customized type transformations and validations. Below, I'll demonstrate how to use them together with a practical example.
Scenario Description
Assume we have an object type for user information, and we want to determine whether certain properties are optional based on the user's permission level.
Defining Base Types
First, define an interface UserInfo for user information:
typescriptinterface UserInfo { id: number; name: string; email: string; role: string; }
Using Mapped Types and Conditional Types
We want to create a new type that determines whether the email property is optional based on the user's role attribute. In this example, we assume that only administrators (admin) must have the email property, while other users have it as optional.
typescripttype ConditionalEmail<T extends UserInfo> = { [K in keyof T]: K extends 'email' ? T['role'] extends 'admin' ? string : string | undefined : T[K] };
Parsing the Type Definition
In the above type definition, we use mapped types to redefine each property:
[K in keyof T]iterates over all properties ofUserInfo.K extends 'email'is a conditional type that checks if the current key isemail.- If it is the
emailkey, we further use the conditional typeT['role'] extends 'admin'to verify ifroleisadmin.- If it is
admin, the type ofemailisstring. - If not, the type of
emailisstring | undefined(i.e., optional).
- If it is
- For non-
emailproperties, we directly use the original typeT[K].
Using This Type
typescriptconst user1: ConditionalEmail<UserInfo> = { id: 1, name: 'Alice', role: 'admin', email: 'alice@example.com' }; const user2: ConditionalEmail<UserInfo> = { id: 2, name: 'Bob', role: 'user' // email is optional, so it can be omitted };
Summary
By using the above approach, we can dynamically adjust the types of other properties based on specific property values within the object. This method is particularly useful when handling complex objects with varying permissions. By combining mapped types and conditional types, TypeScript provides robust type system capabilities that enhance code safety and flexibility.