Hone logo
Hone
Problems

Fluent Interface Builder in TypeScript

Creating fluent interfaces, also known as chainable methods, allows developers to express complex operations in a more readable and concise manner. This challenge asks you to build a utility class in TypeScript that facilitates the creation of such fluent interfaces, enabling you to chain method calls together on a single object. This is a common pattern in libraries and frameworks to improve usability and code clarity.

Problem Description

You are tasked with creating a FluentBuilder class in TypeScript. This class should provide a base structure for building objects with a fluent interface. The FluentBuilder should have a build() method that returns the final constructed object. It should also allow for the addition of custom methods that can be chained. These custom methods should modify the internal state of the builder and return the builder instance itself, allowing for subsequent method calls.

Key Requirements:

  • build() method: This method should return a new object containing the accumulated state from the chained method calls.
  • Custom Method Addition: The builder should have a method to add custom methods to the fluent interface. These methods should accept a name (string) and a function that modifies the builder's internal state and returns the builder instance.
  • Chaining: Custom methods should return the FluentBuilder instance to enable method chaining.
  • Internal State: The builder should maintain an internal state (e.g., an object) to store the values set by the chained methods.

Expected Behavior:

When a custom method is added and then chained, the corresponding logic within the custom method should execute, updating the internal state of the builder. The build() method should then return an object containing the final state.

Edge Cases to Consider:

  • Adding the same method name twice. (Should either throw an error or silently overwrite the existing method - your choice, but document your decision).
  • Methods that modify the internal state in unexpected ways. (The builder class itself doesn't validate the methods, so assume they are well-behaved).
  • Calling build() before any methods are chained. (Should return an object with default values or an empty object - your choice, but document your decision).

Examples

Example 1:

Input:
const builder = new FluentBuilder();
builder.addMethod('name', (builder) => { builder.state.name = 'John Doe'; return builder; })
       .addMethod('age', (builder) => { builder.state.age = 30; return builder; })
       .name()
       .age()
       .build();

Output:
{ name: 'John Doe', age: 30 }

Explanation: The 'name' and 'age' methods are added to the builder.  Chaining `name()` and `age()` sets the `name` and `age` properties in the internal `state` object.  `build()` returns an object containing these properties.

Example 2:

Input:
const builder = new FluentBuilder();
builder.addMethod('city', (builder) => { builder.state.city = 'New York'; return builder; })
       .addMethod('country', (builder) => { builder.state.country = 'USA'; return builder; })
       .build();

Output:
{ city: 'New York', country: 'USA' }

Explanation: The 'city' and 'country' methods are added. Calling `build()` returns an object with the default state, which has been modified by the chained methods.

Example 3: (Edge Case)

Input:
const builder = new FluentBuilder();
builder.addMethod('name', (builder) => { builder.state.name = 'John Doe'; return builder; })
       .addMethod('name', (builder) => { builder.state.name = 'Jane Doe'; return builder; }) // Adding the same method twice
       .name()
       .build();

Output:
{ name: 'Jane Doe' }  // Assuming overwrite behavior

Explanation: The second 'name' method overwrites the first.  `build()` returns an object with the final state.

Constraints

  • The FluentBuilder class should be implemented in TypeScript.
  • The internal state of the builder should be a plain JavaScript object.
  • The addMethod function should accept a string (method name) and a function.
  • The function passed to addMethod should accept a FluentBuilder instance as its argument and return a FluentBuilder instance.
  • The build() method should return a new object, not modify the existing builder instance.

Notes

  • Consider using TypeScript's type system to ensure type safety when adding and calling methods.
  • Think about how to handle potential errors or invalid input.
  • The focus is on the structure and chaining mechanism, not on the specific logic of the methods themselves. The methods are just placeholders for your own custom logic.
  • You can choose to initialize the internal state with default values or leave it empty. Document your choice.
Loading editor...
typescript