0

I don't understand most things about the next code (it's from the typescript handbook):

function classDecorator<T extends { new (...args: any[]): {} }>(
  constructor: T
) {
  return class extends constructor {
    newProperty = "new property";
    hello = "override";
  };
}

@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
    this.hello = m;
  }
}

console.log(new Greeter("world"));

Why in <T extends { new (...args: any[]): {} }>, T extends an "object¿?"? What is exactly what T is extending?

And why that function is returning a class that extends the constructor? I'm very confused and I have trying to understand that 2 days. Thanks.

1
  • 1
    { new (...args: any[]): {} } is a type literal for values that implement new, i.e. which are constructor functions. Commented Sep 5, 2020 at 17:37

1 Answer 1

1

In JavaScript a class is a constructor, that is, a class is an object that you can new.

The syntax for defining a constructor in a class definition is not actually a defining a member of the class, it is just a way to specify parameters and, optionally perform some initialization in an organized manner.

The new keyword predates the ES2015 class syntax some two decades, before the introduction of which "classes" were witten as

function Greeter(m: string) { }

And instantiated using new Greeter("hi").

That is, you wrote a constructor function, and that is what a class was and largely still is.

When the class syntax was added in ES2015, it was designed, for the most part, as a more declarative syntactic sugar for the existing constructor function pattern.

Now, with that context, we can get to the meat of your question.

The constraint on T says that it must be a constructor, that is something that can be newed, and that is a class.

Such a type can be written several ways in TypeScript

As in the example:

{ new (...args: any[]): {} }

Describes an object that can called with new, passing zero or more parameters, that is a class, and that is a constructor.

For clarification, compare this with the type of an object that can be called without new

{ (...args: any[]): {} }

The above describes an object that can called without new, passing zero or more parameters, that it a function.

In modern TypeScript an alternative syntax is usually preferred as it is more concise and expressive

new (...args: any[]) => {}

For a for a class/constructor and

(...args: any[]) => {}

For a function.

As to the decorator's return value, it is leveraging a capability of class decorators to replace the class itself with another class. Class decorators can modify their target or, as we see in this case, replace it my returning a new class.

Sign up to request clarification or add additional context in comments.

5 Comments

But if the decorator returns another constructor, the new constructor for the class will be the constructor that the decorator returned? And if the decorator don't return anything, the constructor for the class will be the same?
That's almost correct. It is replacing the entire class. As I attempted to emphasize, the class and the constructor are one and the same. If it doesn't return anything, the class will not be replaced but the decorator could still modify the class in place, Say by adding a method
Oh, I see.. But why I can not replace <T extends { new (...args: any[]): {} }> with T: Function?
Typescript tries to help you avoid common errors like forgetting to use the new operator on a constructor function or extending a function that isn't meant to be a constructor. Therefore, it differentiates between types that can be called and types that can be called with new. So while the following JavaScript is valid function f() {} new f(), TypeScript will complain because the type it gives to a function doesn't include a new (args): any call signature. While you could bypass that with a type assertion, you should use the more explicit type that denotes a constructor.
Ah okay. Thanks you very much :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.