Type Guarding for Diverse Data Structures
In real-world applications, you often deal with data that can come in various shapes and forms. TypeScript's type system is powerful, but sometimes you need to refine the type of a variable at runtime to ensure you're working with the correct structure. This challenge focuses on implementing custom type guards to safely discriminate between different object types.
Problem Description
You are tasked with building a system that processes different types of user profiles. These profiles can be either AdminProfile or RegularUserProfile. You need to write functions that can safely handle these different profile types by using custom type guards.
What needs to be achieved:
- Define interfaces for
AdminProfileandRegularUserProfile. - Create a function
processUserProfilethat accepts a union type ofAdminProfile | RegularUserProfile. - Inside
processUserProfile, use a custom type guard to determine if the current profile is anAdminProfileor aRegularUserProfile. - Based on the profile type, perform specific actions:
- If it's an
AdminProfile, log a message indicating administrative access and display the admin's uniqueadminId. - If it's a
RegularUserProfile, log a message indicating regular user access and display the user'semail.
- If it's an
Key requirements:
- You must define the
AdminProfileandRegularUserProfileinterfaces. - You must implement a custom type guard function, for example,
isAdminProfile, that returnstrueif an object is anAdminProfileandfalseotherwise. - The
processUserProfilefunction should leverage your custom type guard. - The output messages should be clear and informative.
Expected behavior:
- When
processUserProfileis called with anAdminProfile, it should log a specific message including theadminId. - When
processUserProfileis called with aRegularUserProfile, it should log a specific message including theemail.
Important edge cases to consider:
- What happens if an object that is neither an
AdminProfilenor aRegularUserProfileis passed toprocessUserProfile? Your type guard should handle this gracefully.
Examples
Example 1:
Input: An object representing an admin profile.
const admin: AdminProfile = {
type: "admin",
userId: 101,
adminId: "ADM789",
permissions: ["read", "write"]
};
processUserProfile(admin);
Output:
Processing user profile...
This is an admin profile. Admin ID: ADM789
Explanation: The processUserProfile function receives an AdminProfile. The isAdminProfile type guard correctly identifies it as an admin, and the corresponding log message with the adminId is displayed.
Example 2:
Input: An object representing a regular user profile.
const regularUser: RegularUserProfile = {
type: "user",
userId: 202,
email: "test@example.com",
creationDate: new Date()
};
processUserProfile(regularUser);
Output:
Processing user profile...
This is a regular user profile. Email: test@example.com
Explanation: The processUserProfile function receives a RegularUserProfile. The isAdminProfile type guard correctly identifies it as a regular user (by returning false), and the corresponding log message with the email is displayed.
Example 3: (Edge Case)
Input: An object that does not conform to either profile type.
const unknownProfile = {
someOtherProp: "value"
};
processUserProfile(unknownProfile);
Output:
Processing user profile...
Unknown profile type.
Explanation: The processUserProfile function receives an object that doesn't match the structure of either AdminProfile or RegularUserProfile. The type guard should return false, and since there's no specific handler for "unknown" beyond the default, a generic message or an error could be appropriate (here, a simple "Unknown profile type" is shown).
Constraints
- The
userIdproperty in both profile types must be anumber. - The
typeproperty inAdminProfilemust be the literal string"admin". - The
typeproperty inRegularUserProfilemust be the literal string"user". adminIdmust be astring.emailmust be astring.- The solution should be implemented in TypeScript.
Notes
- Consider using a common property (like
typein the examples) to help differentiate between object types. - A type guard function typically has a return type of
parameterName is Type, whereparameterNameis the name of the parameter being checked andTypeis the specific type it's guarding for. - Think about how to safely access properties that are only present on one of the types.