Exploring type delegation in TypeScript

Here is a scenario where I am using the builder pattern in my code:


export class ValidationBuilderBase implements IValidationBuilder {
    public isRequired(): IValidationBuilder {
        const validationResult = Validators.required(this.baseControl);

        if ((!isNullOrUndefined(validationResult) && !isNullOrUndefined(validationResult.required))) {
            this.baseErrors['required'] = 'This input is required.'; // a ValdationErrors instance
        }

        return this;
    }
}

export interface IValidationBuilder {
    isRequired(): IValidationBuilder;
}

export class StringValidationBuilder implements IValidationBuilder {
    constructor(private readonly builder: Validationbuilderbase) {}

    isRequired(): StringValidationBuilder {
        return this.builder.isRequired() as StringValidationBuilder;
    }
}

// other code, within a function
let builder = new ValidationBuilder(this.formGroup.controls['test']);
let validator: StringValidationBuilder = new StringValidationBuilder(builder);
const result = validator.isRequired(); // expecting the type to be StringValidationBuilder or IValidationBuilder?

During testing with karma jasmine test, validator.isRequired() resolves in a type IValidationBuilder, whereas I was expecting it to be StringValidationBuilder.

This confusion arises because StringValidationBuilder implements the interface and overrides the return type with StringValidationBuilder. So, why is it not returning StringValidationBuilder?

In my IDE (VsCode), the code completion suggests that the return type should be StringValidationBuilder.

Edit

The goal of the following code snippet is to chain string validations:


describe(`String validation chaining`, () => {
    it(`should return no errors from the (valid) chain`, () => {
        control().setValidators(Validators.required);
        control().setValue('This is some test string......');
        const result = validator
            .string
            .ifTrue((val) => false, 'error', 'should not reach here')
            .ifFalse((val) => true, 'error', 'should not reach here')
            .isRequired()
            .maxLengthAllowed(100)
            .minLengthAllowed(0)
            .range(25, 35)
            .build();

        expect(result).toEqual(null);
    });
});

To make it work, I had to modify the code like this:


export class StringValidationBuilder implements IValidationBuilder {
    isRequired(): StringValidationBuilder {
        this.builder.isRequired();
        
        return this;
    }
}

Although this modification made the code work, I still have doubts about whether this is the ideal solution.

Answer №1

Is it possible for this to be StringValidationBuilder? The returned value is a ValidationBuilderBase, which aligns with what the builder represents. Besides, the StringValidationBuilder does not inherit from it.

I think your intent might be to have the StringValidationBuilder extend the ValidationBuilderBase. You can simplify things by utilizing the super call:

export class ValidationBuilderBase implements IValidationBuilder {
    constructor(private controls: any) {}

    public isRequired(): ValidationBuilderBase {
        return this;
    }
}

export interface IValidationBuilder {
    isRequired(): IValidationBuilder;
}

export class StringValidationBuilder extends ValidationBuilderBase {
    constructor(controls: any) {
        super(controls);
    }

    isRequired(): StringValidationBuilder {
      return super.isRequired();
    }
}

const validator = new StringValidationBuilder(this.formGroup.controls['test']);
const result = validator.isRequired();

// result now belongs to StringValidationBuilder

playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with Angular 2: Not receiving events when subscribing to a service's Subject property

Presented below is a service: @Injectable() export class AuthService { public reset: Subject<any>; constructor() { this.reset = new Subject(); } public logout() { this.reset.next('logout'); } } Here is an additional s ...

What is the proper way to implement ref in typescript?

Currently, I am in the process of learning how to use Vue3 + Typescript. Previously, I have developed Vue2 applications using plain JavaScript. In my current project, I am attempting to define a reactive variable within the setup() function: setup() { ...

Exporting several functions within a TypeScript package is advantageous for allowing greater flexibility

Currently, I am in the process of developing an npm package using Typescript that includes a variety of functions. Right now, all the functions are being imported into a file called index.ts and then re-exported immediately: import { functionA, functionB ...

Changing the title dynamically for the Global NavBar in Ionic 2

I have been working with the Nav component in Ionic 2 and I'm facing a challenge. I want to maintain a global header with left and right menus while changing the title dynamically as I navigate through different root pages. Here is the code snippet th ...

Nested self-referencing in Typescript involves a structure where

Please note that the code below has been simplified to highlight a specific issue. The explanation before the code may be lengthy, but it is necessary for clarity. Imagine I have a Foo class that represents a complex object. interface Config { bars:{ ...

Incorporate a fresh element into an object after its initial creation

Hello, I am looking to create an object in JavaScript that includes an array-object field called "Cities." Within each city entry, there should be information such as the city's name, ID, key, and a District array object containing town data for that ...

Angular 6 - Resolving the Issue of 'Function calls are not supported in decorators' in Production Builds

Currently, I'm in the process of developing a cutting-edge Angular 6 application. However, as I was progressing with the development phase and tried to create a prod build using the following command: ng build --prod To my dismay, I encountered a pe ...

Ensuring the correctness of environment variables in Next.js using Zod

After spending the entire day trying to figure it out, I realize that the solution may be simpler than expected. I am currently using the well-known zod library to validate my environment variables and transform data. However, I keep encountering a persis ...

Warning: The attribute 'EyeDropper' is not recognized within the context of 'Window & typeof globalThis'

Attempting to utilize "window.EyeDropper" in a project that combines vue2 and TypeScript. When writing the following code: console.log(window.EyeDropper); An error message is generated by my Vetur plugin: Property 'EyeDropper' does not exist on ...

Utilizing Angular to convert a string array into an array of enum values through an HTTP GET request

I have a list of different user roles defined in my typescript code: enum UserRole { CONSULTANT, MANAGER, ... } There is a REST endpoint /users/id/roles that returns an array of strings representing the roles of a specific user: [ "CONSU ...

Leveraging Angular for Remote Configuration Management

How is everything going with you? I'm attempting to retrieve a configuration that I previously set up in Firebase's remote config using my Angular 15 application. The specific configuration is called "AllowedUsers." Here is the image of th ...

Combine objects by selecting attributes from multiple objects

declare type A = {type: 'TypeA', attr1: string} declare type B = {type: 'TypeB', attr2: string} declare type U = A | B When I use type X = Pick<U, 'type'>, I get: { type: 'TypeA' | 'TypeB' } But I a ...

Setting up Angular Toolkit via npm installation

After successfully installing the UIKit npm package within an Angular 2 CLI project, what steps should I take to utilize it? I have also installed typings for UIKit (@types/uikit), but I am unsure of how to properly import the package into a controller i ...

Attempting to personalize the CSS for a tab within a table in Material design is unsuccessful

Within my Angular 5 application, I have implemented the Material API. One of the components in my project is a table with 2 tabs structured like this: <mat-tab-group> <mat-tab> <mat-table> <!-- content --> </mat- ...

The additional parameters I am trying to append are being overwritten by the set parameters in the httpInterceptor

Issue Description: I have implemented an HttpInterceptor that adds an id and token to all requests when the user's credentials are available. However, I am facing the problem of the interceptor overwriting any additional HttpParams added to a request. ...

Angular 13.0 version is experiencing issues when trying to run the "ng serve" command

After installing the npm module, I encountered a problem while using node.js version 14.17.6. I am a beginner in Angular and struggling to find a solution due to not using the latest Angular CLI version. Any help would be greatly appreciated. Thank you in ...

Issue encountered while utilizing JQueryUI alongside TypeScript and the definition file from DefinitelyTyped

Currently, I'm attempting to incorporate JQueryUI with TypeScript by first installing JQueryUI using npm install jquery-ui-dist, and then installing JQuery with npm install jquery. Additionally, I have included the definition files from DefinitelyType ...

Having trouble with Angular 5's Post function?

Having some trouble with my Angular 5 application and API calls. For some reason, when I add headers to the request, the browser is not recognizing them properly and showing 'OPTION' instead of the actual headers. This is resulting in a 403 respo ...

Angular 2 is throwing an error, stating that Observable is not defined

I'm currently working with Observable and ChangeDetectionStrategy to notify other components about any changes that occur. However, I am encountering an issue where the Observable object addItemStream is coming up as undefined. Can anyone spot what mi ...

Managing a single Angular project with multiple apps that share common features: A guide

Imagine having multiple Angular 'apps' with similar features and functions, where one product needs to be customized for different clients with slightly varying needs. The challenge lies in developing within a single code base. Using feature mod ...