Hone logo
Hone
Problems

Creating Branded Types in TypeScript

Branded types are a powerful technique in TypeScript that allows you to associate a type with a specific string literal type, providing compile-time guarantees about the data's origin or meaning. This challenge will guide you through creating and utilizing branded types to enforce stricter type safety and prevent accidental misuse of data within your application. It's particularly useful when dealing with data from external sources or when you want to clearly indicate the purpose of a specific type.

Problem Description

You are tasked with creating a system for managing user IDs. User IDs are generated by a specific service and should only be used within your application if they are properly validated as originating from that service. You need to implement branded types to ensure that only valid user IDs are used, preventing accidental mixing with IDs from other sources.

Specifically, you need to:

  1. Define a UserID branded type: This type should be a string literal type associated with the string "service-x-user-id". Any string that isn't associated with this literal type should not be considered a valid UserID.
  2. Create a function generateUserID(): This function should return a UserID type. It simulates the user ID generation process.
  3. Create a function validateUserID(id: string): UserID | null: This function takes a string as input and attempts to validate it as a UserID. If the input string matches the "service-x-user-id" literal type, it should return the UserID type. Otherwise, it should return null.
  4. Create a function processUserID(id: UserID): This function takes a UserID as input and logs it to the console. This function should only accept a UserID type, not a regular string.
  5. Demonstrate the usage: Show how to generate a valid UserID, validate it, and then process it. Also, demonstrate what happens when you try to pass an invalid string to processUserID.

Expected Behavior:

  • generateUserID() should always return a valid UserID.
  • validateUserID() should return a UserID if the input is valid, and null otherwise.
  • processUserID() should only accept UserID types and log them. Attempting to pass a regular string should result in a TypeScript compile-time error.

Examples

Example 1:

Input: generateUserID()
Output: "service-x-user-id"
Explanation: The generateUserID function returns a string literal type representing a valid UserID.

Example 2:

Input: validateUserID("service-x-user-id")
Output: "service-x-user-id"
Explanation: The validateUserID function correctly identifies a valid UserID and returns it.

Example 3:

Input: validateUserID("invalid-user-id")
Output: null
Explanation: The validateUserID function correctly identifies an invalid UserID and returns null.

Constraints

  • The UserID branded type must be created using TypeScript's string literal type capabilities.
  • The validateUserID function must return null for invalid IDs.
  • The processUserID function must enforce type safety, preventing the use of non-UserID strings.
  • All functions must be written in TypeScript.

Notes

  • Consider using type assertions or type guards to achieve the desired type safety.
  • The goal is to demonstrate the concept of branded types and how they can be used to enforce stricter type checking.
  • Think about how branded types can help prevent errors in larger, more complex applications. They are particularly useful when dealing with data from external APIs or databases where the data types might not be strictly enforced.
Loading editor...
typescript