Type-Safe Builder Pattern in TypeScript
The Builder pattern is a creational design pattern that allows you to construct complex objects step-by-step. In TypeScript, achieving type safety within a builder is crucial to prevent errors and ensure the final object conforms to a defined structure. This challenge asks you to implement a type-safe builder for a Product object, ensuring that all required properties are set before the object is finalized.
Problem Description
You need to create a ProductBuilder class in TypeScript that allows constructing Product objects in a controlled and type-safe manner. The Product object has the following properties:
name:string(required)price:number(required, must be positive)description:string(optional)category:string(optional)tags:string[](optional)
The ProductBuilder should provide methods for setting each property of the Product object. It should also include a build() method that returns a fully constructed Product object. The build() method should throw an error if any required properties (name, price) are not set.
Key Requirements:
- Type Safety: The builder should enforce type safety for all properties.
- Required Properties: The
build()method must ensure that all required properties are set. - Positive Price: The
priceproperty must be a positive number. - Immutability (Optional but Recommended): While not strictly required, consider making the builder methods return the builder instance itself to allow for method chaining.
Expected Behavior:
- Calling builder methods should update the internal state of the builder.
- Calling
build()with all required properties set should return aProductobject with the specified properties. - Calling
build()with missing required properties should throw an error with a descriptive message. - Setting a non-positive price should throw an error.
Edge Cases to Consider:
- What happens if
tagsis not an array of strings? - What happens if
priceis negative or zero? - How to handle optional properties gracefully?
Examples
Example 1:
Input:
builder.setName("Awesome Widget").setPrice(29.99).setDescription("A truly awesome widget").build()
Output:
{ name: "Awesome Widget", price: 29.99, description: "A truly awesome widget", category: undefined, tags: undefined }
Explanation: A Product object is created with the specified name, price, and description. Optional properties are undefined.
Example 2:
Input:
builder.setName("Basic Gadget").setPrice(-5).build()
Output:
Error: Price must be a positive number.
Explanation: An error is thrown because the price is negative.
Example 3:
Input:
builder.setPrice(10).build()
Output:
Error: Name is required.
Explanation: An error is thrown because the name is missing.
Constraints
- The
ProductandProductBuilderclasses must be defined in TypeScript. - The
build()method must throw an error if required properties are missing. - The
priceproperty must be a positive number. - The
tagsproperty must be an array of strings. - The solution should be well-structured and readable.
Notes
- Consider using TypeScript's type system to enforce type safety and ensure that the builder methods are used correctly.
- Think about how to handle optional properties in a clean and type-safe way.
- While immutability isn't strictly required, it's a good practice to make the builder methods return the builder instance itself. This allows for method chaining, which can improve code readability.
- Focus on creating a robust and type-safe builder that can handle various scenarios.