Return the subclass from the constructor function

class X{
    constructor(input: string) {
        // do things
    }
    f() {console.log("X")}
}    
class Y extends X{
    constructor(input: string) {
        // do things
    }
    f() {console.log("Y")}
}
class Z extends X{
    constructor(input: string) {
        // do things
    }
    f() {console.log("Z")}
}

The current arrangement looks like this. It may seem unusual, but I am interested in having new X(inp) return an instance of either Y or Z based on the input (even if it's technically returned as X in TypeScript). This way, calling (new X("Y")).f() would output "Y", and the same for passing "Z", with "X" being the default behavior. Is there a way to achieve this without causing major violations? Right now, I have implemented a static method in X to handle this functionality, although using the constructor might be a cleaner option.

Answer №1

When a class constructor method explicitly uses a return statement to return an object that is not this, the object returned will be received when the class constructor is called with the new operator, instead of the new object created by the constructor.

class Foo {
  fooProp: string;
  constructor() {
    this.fooProp = "abc";
    return {
      fooProp: "zyx",
    }
  };
}
const f = new Foo();
console.log(f.fooProp) // Output: zyx

This scenario is not common and can lead to unexpected outcomes, such as instances not being recognized as part of their own constructor:

console.log(new Foo() instanceof Foo) // Output: false

It is advised to avoid utilizing this pattern unless necessary for specific reasons.

In TypeScript, it is possible to use return in a constructor method if the returned object belongs to a type compatible with the class type. This approach is acceptable when returning subclasses because they are directly related to the parent class.


One way to implement this behavior is demonstrated below:

class A {
  constructor(input?: string) {
    if (input === "B") return new B();
    if (input === "C") return new C();
  }
  f() { console.log("A") }
}
class B extends A {
  constructor() {
    super()
  }
  f() { console.log("B") }
}
class C extends A {
  f() { console.log("C") }
}

const a = new A().f(); // Output: A
const b = new A("B").f(); // Output: B
const c = new A("C").f(); // Output: C

By making A's constructor parameter optional and creating B and C without constructor parameters, you can selectively return subclass instances based on input strings while maintaining hierarchy within the classes.

// Testing the implementation:
const a = new A();
a.f(); // Output: A
const b = new A("B");
b.f(); // Output: B
const c = new A("C");
c.f(); // Output: C

console.log(a instanceof A); // true
console.log(b instanceof A); // true
console.log(c instanceof A); // true

This setup ensures expected results with clear distinctions between direct instances of A and its subclasses.


Although effective, using A's constructor for dual purposes can lead to confusion and errors. Consider making A's constructor a protected method and employing a static method for selecting the appropriate constructor to call, as illustrated below:

class A {
  protected constructor() { }
  f() { console.log("A") }
  public static make(input?: string) {
    if (input === "B") return new B();
    if (input === "C") return new C();
    return new A();
  }
}
class B extends A {
  f() { console.log("B") }
}
class C extends A {
  f() { console.log("C") }
}

const a = A.make();
a.f(); // Output: A
const b = A.make("B");
b.f(); // Output: B
const c = A.make("C");
c.f(); // Output: C

console.log(a instanceof A); // true
console.log(b instanceof A); // true
console.log(c instanceof A); // true

Despite slight differences in usage syntax, this method provides a clearer understanding of class instantiation choices for both internal implementations and external calls.

Explore the code in TypeScript 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

Leverage C# model classes within your Angular application

Just wanted to express my gratitude in advance import { Component, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-fetch-data', templateUrl: './fetch-data. ...

Creating a searchable and filterable singleSelect column in the MUI DataGrid: A step-by-step guide

After three days of working on this, I feel like I'm going in circles. My current task involves fetching data from two API sources (json files) using the useEffect hook and storing them in an array. This array contains a large number of products and a ...

Building a NestJS/Prisma RESTful API to retrieve data from a related field

I am diving into creating my very own Rest API using Nestjs and Prisma for the first time. This project is a basic representation of an inventory management system, keeping things simple with shelves and boxes to store items. The structure in place has tab ...

Show blank value if there are no search results, along with an additional menu option

I am currently working on a Typeahead feature with a customized menu using the renderMenu method. In this setup, I have added 2 custom menu items at the bottom - one divider and a button. An issue arises when there are no search results. If I do not inclu ...

A guide on cycling through keys in an object with changing values using Typescript

Struggling with a beginner question here - I'm having trouble iterating through an object with dynamic keys in Typescript //This is how I've typed my object let obj: { [key: string]: string } = {}; Using forEach or map isn't working and thr ...

When another page is refreshed, Angular routing redirects back to the home page

Within my application, there is a homepage that appears after the user logs in, along with some other pages. However, I've encountered an issue where when I navigate to one of these other pages and then refresh the page, it redirects me back to the ho ...

The object literal is limited to defining recognized properties, and 'clientId' is not present in the 'RatesWhereUniqueInput' type

Currently, I am using typescript alongside prisma and typegraphql in my project. However, I have encountered a type error while working with RatesWhereUniqueInput generated by prisma. This input is classified as a "CompoundUniqueInput" due to the database ...

No data found in req.query object in ExpressJS

When I use http.post to send data from Angular to NodeJS, the req.query always comes back empty for me. Here is my server.js setup: const express = require('express'); const cors = require('cors'); const bodyParser = require('body ...

Using Conditional Types in Supabase Query results in the TypeScript error, "Type is incompatible with type 'never'."

Query I have encountered a TypeScript issue while working on a Next.js project with Supabase as the backend. To handle responses from Supabase queries, I created some helper types, but I'm stuck on resolving this problem. Helper Types Overview: Belo ...

Ways to eliminate the white background gap between pages on ionic

While developing an app using Ionic, I encountered a strange issue. Everything runs smoothly on a browser, but when testing the app on an Android 5 device, I noticed a white background appearing between pages. The app loads correctly with the custom splas ...

Ensure that the method is triggered

I have a builder class that implements an interface which it is expected to build. However, I would like to enforce one method of this class to be called at compile time, rather than runtime. The class is designed to be used as a chain of method calls and ...

The compatibility between `webpack-streams` and `@types/webpack`

I encountered an issue with my gulpfile while building it with TypeScript. Everything was working fine until I included @types/webpack-stream. Suddenly, I started getting a series of errors that looked quite ugly: node_modules/@types/webpack-stream/index.d ...

The type of Object.values() is not determined by a union or Records

When utilizing TypeScript, the Object.values() function encounters issues in deducing the accurate type from a union of Records: type A = Record<string, number>; type B = Record<string, boolean>; function func(value: A | B) { const propert ...

What are some ways to incorporate advanced/nested type variables when using arrow functions?

Is there a way to use "advanced/nested" type variables, similar to how T is utilized in this function declaration, when working with arrow functions? function wrapInObject<T>(key: string) { return (x: T) => ({ [key]: x }); } I attempted to ach ...

Accessing a TypeScript variable in Angular2 and binding it to the HTML DOM

While I have experience with AngularJS, delving into Angular2 has proven to be a new challenge for me. Understanding the ropes is still a work in progress. In my list of files, there's a home.ts and a home.html Within my home.ts, this snippet reside ...

Developing a Universal Type in Typescript

Encountered an issue with generic types while working on a user-defined type(interface) structured like this: IList1: { prop1: string, prop2: number, prop3: string . . . } ILi ...

Angular HTTP client fails to communicate with Spring controller

Encountered a peculiar issue in my Angular application where the HttpClient fails to communicate effectively with the Spring Controller. Despite configuring proper endpoints and methods in the Spring Controller, the Angular service using HttpClient doesn&a ...

What is the best way to categorize a collection of objects within a string based on their distinct properties?

I am working with an array of hundreds of objects in JavaScript, each object follows this structure : object1 = { objectClass : Car, parentClass : Vehicle, name : BMW } object2 = { objectClass : Bicycle, parentClass : Vehicle, name : Giant } object3 = { ob ...

The API request does not provide randomized results and does not show any display

I am facing an issue with a button that is supposed to fetch a random user from an API. When I retrieve all the users, the information is displayed correctly. However, when I try to select a user at random, it does not work as expected. Also, it seems to a ...

What is the most efficient method to retrieve an API in Angular?

Recently, I dedicated some time to a personal Angular project. While working on it, I decided to experiment with making an API call to PokeAPI in order to retrieve the .svg image of a Pokemon along with its name. After successfully implementing this featur ...