The 'any' type is not compatible with constructor functions

I am currently working on implementing a class decorator in Typescript. I have a function that accepts a class as an argument.

const createDecorator = function () {
  return function (inputClass: any) {
    return class NewExtendedClass extends inputClass {}
  };
};

The objective is to be able to utilize it in the following manner:

@createDecorator()
class AnotherClass{}

However, I keep encountering the error

type any is not a constructor function type
. Any suggestions on how I can accomplish my goal?

Answer №1

Why is this code not working even though it's valid JavaScript?

The issue lies in the fact that while the generated JavaScript may be valid and functional, TypeScript operates under stricter rules. TypeScript will flag any code that does not adhere to its guidelines, even if it ultimately compiles the code into JavaScript without errors.

In this scenario, TypeScript lacks support for extending classes through decorators. This means that while the resulting JavaScript functions correctly, TypeScript cannot fully comprehend the process when using decorators in this manner.

For instance:

const decorate = () => (target: typeof Cat) =>
{
    return class Lion extends target
    {
        public Roar = () => console.log("Roaar")
    }
}

@decorate()
class Cat
{
    public Purr = () => console.log("Purr purr");
}

var lion = new Cat();
lion.Roar(); // Error in TypeScript but working in JS

While operationally sound, TypeScript struggles to grasp the class mutation within the decorator, leading to confusion over the relationship between Cat and Lion.

There have been discussions around enhancing TypeScript to understand class mutations in decorators, but currently, this feature is unavailable.

To avoid potential conflicts, it is advisable to refrain from altering classes through decorators in this way. Even if it were feasible, the current example lacks clarity, making direct extension of base properties a more straightforward approach:

Something extends MyReusableProperties

Answer №2

Here's an alternate approach you could consider:

interface NewInstance<T> {
  new (...args: any[]): T;
}

function decorate()
{
  return function (target: NewInstance<Object>)
  {
    return class ExtendedClass extends target {
      public additionalProperty: string;
      constructor() { 
        this.additionalProperty = "test2";
      }    
    }
  };
};

However, this may lead to a new issue:

// Error: Type 'typeof ExtendedClass' is 
// not assignable to type 'typeof AnotherThing'.
@decorate() 
class AnotherThing{
  public someProp: string;
  constructor() { 
    this.someProp = "test";
  }
}

To address the error, you can try this:

function decorate()
{
  return function (target: NewInstance<Object>)
  {
    class ExtendedClass extends target {
      public additionalProperty: string;
      constructor() { 
        this.additionalProperty = "test2";
      }
    }
    return (<any>ExtendedClass); // casting required!
  };
};

Another error may surface after that:

let anotherThing = new AnotherThing();
console.log(anotherThing.someProp);

// Property 'additionalProperty' does not 
// exist on type 'AnotherThing'.
console.log(anotherThing.additionalProperty);

To resolve this, you might have to resort to using any:

console.log((<any>anotherThing).additionalProperty); // More casting!

This workaround may not be ideal. You could explore a different strategy like this:

interface NewInstance<T> {
  new (...args: any[]): T;
}

function decorate()
{
  return function (target: NewInstance<Object>)
  {
    class ExtendedClass extends target {
      public additionalProperty: string;
      constructor() { 
        this.additionalProperty = "test2";
      }
    }
    return (<any>ExtendedClass);
  };
};

function applyDecorator<T,TDecorated>(decorator, ctr): NewInstance<TDecorated> {
  let decorated: NewInstance<TDecorated> = <any>decorator()(ctr);
}

interface IAnotherThing {
  someProp: string;
}

interface ISomethingElse extends AnotherThing {
  additionalProperty: string;
}

class AnotherThing implements IAnotherThing {
  public someProp: string;
  constructor() { 
    this.someProp = "test";
  }
}

let SomethingElse = applyDecorator<IAnotherThing, ISomethingElse>(decorate, AnotherThing);

let anotherThing = new SomethingElse();
console.log(anotherThing.someProp);
console.log(anotherThing.additionalProperty);

Hope this offers some helpful insights :)

Answer №3

It seems like the issue lies in the signature of your decorator function. You need to modify the function signature from function ( target: any ) to

function <T extends { new (...args: any[]): {} }>
. To simplify, for the statement
class myExtendClass extends target
to work without errors, target must be a class or a constructor function as referred to in Javascript. You are not able to extend to any, but only to a class (constructor function). By using generic type T with constraints, you ensure that target is constrained to be a class (constructor function). Therefore, the extend statement will execute without any issues. Here's an example:

const decorate = function ()
{
    return function <T extends { new (...args: any[]): {} }>
        (target: T)
  {
      return class myExtendClass extends target{
          anotherProperty = 10; //assigning a class property here
          propertyFromDecorator = "new property from decorator"; // new property
    }
  };
};


@decorate()
class Something{
    myProp: string;
    anotherProperty:number;
    constructor() {
        this.myProp = "my property";
    }
} 

var someThing = new Something();
console.log(someThing);

For more information, please refer to the Typescript Decorators page on Github

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

Error in React Router when using TypeScript

Encountering errors while trying to set up router with React and TypeScript. https://i.sstatic.net/muSZU.png I have already attempted to install npm install @types/history However, the issue persists. Your assistance would be greatly appreciated. Thank y ...

Encountering difficulties while attempting to transition from angular 9 to angular 10

I attempted to upgrade my Angular project by running the following commands: $ ng update @angular/core@9 @angular/cli@9 $ ng update @angular/core @angular/cli However, when I executed the last command in the console, it resulted in an error message: Your ...

What is the best way for a parent process to interrupt a child_process using a command?

I'm currently in the process of working on a project that involves having the user click on an 'execute' button to trigger a child_process running in the backend to handle a time-consuming task. The code snippet for this operation is shown b ...

"Perform an upsert operation with TypeORM to create a new entry if it

Is there a built-in feature in TypeORM to handle this scenario efficiently? let contraption = await thingRepository.findOne({ name : "Contraption" }); if(!contraption) // Create if not exist { let newThing = new Thing(); newThing.name = "Contrapt ...

Retrieve a single user using a query in the GraphQL platform

I've been struggling to create a GraphQL query in Express that retrieves only one user instead of all users every time. This is the query I'm using, incorporating TypeORM as my ORM: import { GraphQLList, GraphQLID } from 'graphql'; imp ...

In TypeScript, deduce the optional generic type automatically

Feeling a bit out of my depth here. I need to perform an inference on a generic that includes an optional "parse" function which returns the formatted value [or throws]. They say code speaks louder than words, so let's take a look at the example: exp ...

Retrieve distinct values for the keys from an object array in JavaScript

Here is the structure of my array: const arr1 = [ { "Param1": "20", "Param2": ""8", "Param3": "11", "Param4": "4", "Param5": "18", ...

Leveraging vue-devtools in combination with the composition-api while implementing a render function (such as JSX)

Ever since I made the transition to utilizing the composition-api and incorporating a render function (primarily leveraging JSX with TypeScript for type safety within the template), I've encountered an issue where vue-devtools cannot inspect the compu ...

The type does not meet the requirements set by the class it is inheriting from

Currently, I am in the process of working on a WebSocket Secure (WSS) server utilizing the Node ws library which can be found here. More specifically, I am following the approach outlined in this particular question, although its relevance is yet to be det ...

Sharing interfaces and classes between frontend (Angular) and backend development in TypeScript

In my current project, I have a monorepo consisting of a Frontend (Angular) and a Backend (built with NestJS, which is based on NodeJS). I am looking to implement custom interfaces and classes for both the frontend and backend. For example, creating DTOs s ...

Is there a way to establish a boundary for the forEach function in TypeScript?

In my web-based game, I use the forEach command to retrieve the team players in the lobby. However, for a specific feature in my game, I need to extract the id of the first player from the opposing team. How can I modify my current code to achieve this? T ...

The property 'filter' is not recognized on the 'Object' type. An attempt to filter the response was made

Trying to fetch data from a JSON file that matches the player's name in the URL, such as localhost:4200/players/Febiven, should only retrieve information about Febiven. The code is written in Angular 6. The current code snippet is as follows: player ...

Modifying elements in an array using iteration in typescript

I'm trying to figure out how to iterate over an array in TypeScript and modify the iterator if necessary. The TypeScript logic I have so far looks like this: for (let list_item of list) { if (list_item matches condition) { modify(list_ite ...

"Exploring the world of Ionic 2: uncovering its public variables

I'm facing an issue with Ionic 2, specifically with Angular. My concern revolves around a variable called "isConnected". I am unable to access it from an internal function within a function as it gives me an error saying "can't define property of ...

Radio buttons with multiple levels

Looking to implement a unique two-level radio button feature for a specific option only. Currently, I have written a logic that will display additional radio buttons under the 'Spring' option. However, the issue is that when it's selected, t ...

Having difficulty deciphering the legend in the Highcharts library for Angular (angular-highcharts)

I have a requirement to display two datasets as dual column charts. (2) [{…}, {…}] 0: historyDate: "2021-02-10T10:00:000Z" documentStatusHistory: CANCELLED: 6 COMPLETED: 52 IN_PROGRESS: 1 OPEN: 1 ...

Avoid generating file modifications due to a version update when using OpenApiGenerator

When utilizing the typescript-rxjs generator, the issue arises when generating a new version of API clients. The majority of files are altered due to a simple version change: * The version of the OpenAPI document: 1.47.0-rc.20. This results in real change ...

The push() method replaces the last item in an array with another item

Two objects are available: ej= { name="", code: "", namebusinessG:"", codebusinessG:"" }; group = { name:"", code:"" } Both of these objects will be stored in two arrays: groupList:any[]=[]; ejList:any[]=[]; The program flow s ...

replace the tsconfig.json file with the one provided in the package

While working on my React app and installing a third-party package using TypeScript, I encountered an error message that said: Class constructor Name cannot be invoked without 'new' I attempted to declare a variable with 'new', but tha ...

Create an eye-catching hexagon shape in CSS/SCSS with rounded corners, a transparent backdrop, and a

I've been working on recreating a design using HTML, CSS/SCSS in Angular. The design can be viewed here: NFT Landing Page Design Here is a snippet of the code I have implemented so far (Typescript, SCSS, HTML): [Code here] [CSS styles here] [H ...