I already know how to add "newable" (i.e. has constructor) constraint for function arguments (like argument for foo
function below), but same techique doesn't apply to generic type parameters.
Why is that and how to fix it ?
type NoParameterCtor<T> = { new(): T }
function foo<T>(ctor: NoParameterCtor<T>) { }
interface Bar<T extends NoParameterCtor<T>> { }
class Zoo { }
foo(Zoo)
// no compiler error
class Zar implements Bar<Zoo> { }
// Type 'Zoo' does not satisfy the constraint 'NoParameterCtor<Zoo>'
As mentioned in the comments, T extends NoParameterCtor<T>
is an unusual constraint that means "T
is a constructor that makes new instances of itself". Unless you are trying to describe self-replicating constructors, this is not what you mean.
If you just want T
do be "anything newable", then you don't need to care about the instance type. Assuming you're using TS3.0 or later you can use unknown
to mean any type, although you can use also any
. So perhaps you want Bar
to be
interface Bar<T extends NoParameterCtor<unknown>> { }
The following still won't work though:
class Zar implements Bar<Zoo> { } // error!
// Zoo does not satisfy the constraint NoParameterCtor<unknown>
That's because the type Zoo
is not newable; it's the instance type of the Zoo
class. I don't know if you're confused about the difference between named values and named types in TypeScript, but you're in good company if so. In short, class Zoo {}
introduces a type named Zoo
, which is the type of instances of the class, and a value named Zoo
, which is the constructor of such instances. And the type of the Zoo
value is not the Zoo
type. To refer to the type of the Zoo
constructor value, you need to use typeof Foo
instead:
class Zar implements Bar<typeof Zoo> { } // okay
Also I assume you've stripped out the contents of Bar
, Zar
and Zoo
because they're not relevant here. But just to be clear, empty interfaces match just about everything because TypeScript uses structural typing. If Bar
needs access to the instance type of T
, then you can use the built-in library type alias InstanceType<>
to get it:
interface Bar<T extends NoParameterCtor<unknown>> {
theConstructor: T,
theInstance: InstanceType<T>
}
class Zar implements Bar<typeof Zoo> {
theConstructor = Zoo; // the constructor
theInstance = new Zoo(); // an instance
}
Hope that helps. Good luck!