Confusion with using "this-typing" in Typescript

In reference to "this-typing" mentioned here and here, is it understood that the use of this as a type represents the current class or object specified after the dot (which allows inherited methods to refer to their own class rather than the parent's class).

However, why does the following code not function as expected:

class Test {
    children: Array<this>;

    constructor() {
        this.children = [new Test()];
    }
}

(The intention is to achieve this with an inherited class, but it doesn't seem to work with a base class. Given that this is of type Test, why can't children be an array of Test objects?)

Answer №1

Using this as a type in TypeScript refers to the instance rather than the class.
This concept is known as Polymorphic this types and can be implemented as shown below:

class Point {}

class Point2D extends Point {
    constructor(public x: number, public y: number) {
        super();
    }
}

class Point3D extends Point2D {
    constructor(x: number, y: number, public z: number) {
        super(x, y);
    }
}

class Builder2D {
    protected _x: number;
    protected _y: number;

    x(x: number): this {
        this._x = x;
        return this;
    }

    y(y: number): this {
        this._y = y;
        return this;
    }

    build(): Point {
        return new Point2D(this._x, this._y);
    }
}

class Builder3D extends Builder2D {
    private _z: number;

    z(z: number): this {
        this._z = z;
        return this;
    }

    build(): Point3D {
        return new Point3D(this._x, this._y, this._z);
    }
}

let p1 = new Builder3D().x(0).y(0).z(0).build();

If both Builder2D.x() and Builder2D.y() were to return Builder2D:

x(x: number): Builder2D {
    this._x = x;
    return this;
}

y(y: number): Builder2D {
    this._y = y;
    return this;
}

Then there would be a failure with the code snippet provided.

let p1 = new Builder3D().x(0).y(0).z(0).build();

The error displayed would be:

Property 'z' does not exist on type 'Builder2D'

In such a scenario, it's important to not always return this. Rather, you can consider implementing the following logic to handle instances differently based on their types:

class Test {
    public children: Array<Test>;

    constructor() {
        this.children = [new Test()];
    }
}

interface OtherTest {
    children: Array<OtherTest>;
}
class OtherTest extends Test {
    constructor() {
        super();
        this.children.push(new Test(), new OtherTest());
    }
}

let t1 = new Test();
let c1 = t1.children[0]; // typeof c1 is Test

let t2 = new OtherTest();
let c2 = t2.children[0]; // typeof c2 is OtherTest

This demonstrates a way to manage different child instances within a parent class hierarchy.


Edit

There appears to be an open issue regarding Polymorphic "this" for static members on the TypeScript repository: Polymorphic "this" for static members.

Answer №2

Defining a derived class:

class TestDerived extends Test {
    someMethod():void { }
}

As previously mentioned, this in the current class refers to the class itself. So, the children member of TestDerived is of type TestDerived[]. This allows us to do:

let b = new TestDerived ();
b.children[0].someMethod();

If TypeScript were to allow us to populate this array (in the constructor of the superclass) with instances of Test, it would lead to a loss of type safety since someMethod is not defined in Test.

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

When the child component's form is marked as dirty, the parent component can access it

I have implemented a feature in my application that notifies users about pending changes on a form before they navigate away. Everything works as expected, but I have a child component with its own form that needs to be accessed by the guard to check if i ...

Contrary to GraphQLNonNull

I am currently working on implementing GraphQL and I have encountered a problem. Here is an example of the code I wrote for GraphQL: export const menuItemDataType = new GraphQL.GraphQLObjectType({ name: 'MenuItemData', fields: () => ...

Ways to maintain data returned by forkJoin

I encountered a situation where I needed to retrieve results from 2 HTTP calls and combine them into an array that would be passed to a class instantiation. Below is the code snippet: export class VideoListComponent implements OnInit { @ViewChild(MatPag ...

Swapping out a setTimeout() function for an Observable in an Angular Reactive Form

I am looking to upgrade from using a setTimout() to implementing an Observable in an Angular reactive form. Within a grandchild component of mine that handles a section of a larger reactive form, I fetch an array of data objects from an API endpoint. This ...

Is there a way to incorporate margins into a React component using TypeScript?

I am currently facing a challenge in passing CSS attributes to a component that I am working with. The reason behind this is that I need to modify the margins to fit a specific space, hence my attempt to directly pass the margins. Does anyone have any sug ...

Unexpected behavior when Antd Custom validator and validateFields are used in combination

I am currently developing a form using Ant Design's Form component, and I have a requirement for custom validation of the phone number field. The validation depends on the input in the country field and involves additional logic. While other fields ar ...

Guide to customizing the value appearance in tooltip axispointer chart (angular)

Check out the code snippet here: https://stackblitz.com/edit/angular-ngx-echarts-zn8tzx?file=src/app/app.component.ts view image description I am looking to format and add the text "UPLOAD" and "DOWNLOAD" below the date and time. For instance: 2020-02- ...

What is the process for creating a React Component with partially applied props?

I am struggling with a function that takes a React component and partially applies its props. This approach is commonly used to provide components with themes from consumers. Essentially, it transforms <FancyComponent theme="black" text="blah"/> int ...

Shifting Angular Component Declarations to a New Location

Here's a question that might sound silly: In my Angular project, I am looking to reorganize my component declarations by moving them from angular.module.ts to modules/modules.modules.ts. The goal is to structure my src/app directory as follows: src ...

Encountered a routing error when setting up routes in Angular

I am currently learning Angular and encountering an issue while setting up routes in my Angular application. The error occurs in the app.module.ts file when trying to declare the routes service within RouterModule.forRoot(). I need assistance with determin ...

What causes the intermittent failure of auto-import in VS Code?

My experience with auto-completing in VS Code has been inconsistent when it comes to auto-importing. Sometimes it successfully imports, but other times it fails, even with both local and node_modules imports. This issue occurs specifically in TypeScript fi ...

Adding an error or validation message to the renderer within a React hook - a quick and easy guide

{ label: "Room", name: "room", type: "select", rule: yup.string().required(), renderer: (data: any) => { const { control, register, errors } = useFormContext(); return ( <SelectPicker plac ...

Referencing other styled-components in Typescript and React code bases

I'm attempting to replicate this code snippet found on https://styled-components.com/docs/advanced using TypeScript. const Link = styled.a` display: flex; align-items: center; padding: 5px 10px; background: papayawhip; color: palevioletred; ...

Sending data dynamically does not work in Angular forms

I am facing an issue with dynamically capturing text and sending it to my email. While manually typing into TS onSendEmail, it works fine. I am using a free email hosting server, and all the required files are available in the assets folder. HTML code < ...

Smooth sailing: Angular NodeJs app operating flawlessly in local environment, hits snag upon deployment to Heroku

I have encountered a perplexing issue with my application deployed on Heroku. While the application runs smoothly locally, I am facing a troublesome error in the dev deployment branch. Despite conducting extensive research and investing significant time in ...

React failed to render all rows, only displaying one object repeatedly

It is expected to populate all rows in the table (the rows come from the API as an array of objects). Currently, the issue is that only the last object of the array is being rendered, and it is repeating. As shown in the screenshot, only one object is load ...

Querying with a TypeORM many-to-many relationship

Entity Program: export abstract class ProgramBase { @Column('varchar') programName: string; @Column('text') programDescription: string; @Column({ type: 'boolean', default: false }) isDeleted: boolean; @Column( ...

Modify the MUI time picker to display as a digital clock inside a DateTimePicker widget

I need to update my MUI DateTimePicker component to use the DigitalClock time picker instead of the Analog Clock picker. The current component has two steps, first picking the date from a calendar and then selecting the time. This change is only necessary ...

Issue: Control with the specified name '0' could not be located

Kindly review this code snippet and assist me in resolving the issue. I have set up a formArray where the label and formControlName do not match, so I have utilized mapping to synchronize them. Upon receiving a response from the API, I initialize the for ...

Express displays html instead of json when error handling occurs

I recently followed a tutorial on Express.js to create a simple error handler. function clientErrorHandler(err, req, res, next) { if (req.xhr) { console.log('clienterrorhandler', err); res.status(500).send({ error: 'Something faile ...