-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generic { new(...args: any[]): T }
parameter doesn't work for abstract classes
#5843
Comments
As a guess, the error seems to suggest there is a way to denote an "abstract constructor type" (eg |
I interpreted the error to mean that |
the error is that function makeInstance(constructor: new()=> T): T {
return new constructor();
}
makeInstace(B); // OK
makeInstace(A); // Error as you would expect i think we need a way to enable the scenario, though this needs a propsal |
We've discussed this issue elsewhere, and this is not the only thing a construct signature is used for. One specific scenario is checking |
Right, I got burned by that a few times when learning TypeScript - this is why I only use the |
I also just ran into this when trying to find a way to use abstract classes in the context of a DI container. Something like: registerType<T>(base: AbstractClass<T>, impl: ConcreteClass<T>);
interface ConcreteClass<T> {
new (...args: any[]): T;
}
interface AbstractClass<T> {
// ???
} |
+1 for this issue, I have a real-life scenario as well where I have a function which accepts constructors but only for reflection purposes; e.g. it does instanceof checks but nothing else with the constructor, just like the above examply: function isType<T>(value: any, key: { new(...args: any[]): T }): boolean {
return value instanceof key;
} Currently I have to cast the type to |
^^ Same, I have a real life scenario; an Angular component factory that accepts a controller constructor as an argument, and this constructor is always a child of an abstract base. @thorn0 suggested a very neat solution which works very well in my case, but I do consider the inability to specify an abstract with this syntax, while it compiles cleanly with interface syntax. |
Passing a constructor reference is the only way I know to materialize a parameterized type in a function signature per issue #4890. This restriction on passing a constructor reference constrains useful type checking. |
I also encounter this for a DI: container.registerInstance(AbstractClass, someInstance)
const instance = container.get(AbstractClass)
instance === someInstance // true
// where
class Container {
get<T>(key: new (...args: any[]) => T): T
} |
@unional As mentioned in my linked issue above, you can do the following and everything works as expected: class Container {
get<T>(key: (new (...args: any[]) => T) | Function): T
}
abstract class AbstractClass {}
const instance = container.get(AbstractClass); // is of type AbstractClass |
@hediet It looks like a quirk in the type inference system. If But even if (surprisingly) it turns out not to be a quirk, adding |
According to #13907 this behavior does not seem to be a quirk. I would interpret their answer as "Yes, you can build upon this behavior". TypeScript's typesystem isn't sound anyways - so there are unavoidable holes. Everything else is just a compromise. I would prefer a typed |
My use case is mixin applied to abstract class: type Constructor<T> = new(...args: any[]) => T;
abstract class AbstractBase {}
function Mixin<T extends Constructor<object>>(Base: T) {
return class extends Base {};
}
class Derived1 extends Mixin(AbstractBase) {} // error
class Derived2 extends Mixin(AbstractBase as Constructor<AbstractBase>) {} Currently I need to cast |
@vojtechhabarta my case exactly. |
Just ran into this. I too would like some clarification on what @amir-arad asked regarding the use of Edit: because |
@AdamWillden I didn't get the explanation in the last edit, but I think I agree with the first notion. an abstract class is a class for all intents except that you can't use it directly with However you should be able to inherit it and call it as so maybe add an optional explicit super signature, and allow the to clarify, here's some pseudo-types following @sccolbert 's code example: interface Class<T> {
super (...args: any[]): T; // this is the signature for calling it as `super` from a child class. if it's not declared, it's automatically inferred from the new signature
}
interface ConcreteClass<T> extends Class<T>{
new : typeof this[super];
} one can also think of something like this: interface FinalClass<T>{
super: never;
new : (...args: any[]): T;
} but I really don't know how I feel about that |
I encountered this very similar error (my error code is TS2684 but a nearly identical error message).
I'm attempting to build an entity controller similar to .NET Web API. I'm making use of
Making a concrete type first and having
It would seem to me that either
|
@vojtechhabarta wrote:
Upon receiving |
It's somewhat unfortunate that we don't have a type that means "a valid RHS to The general principle that, no, you can't construct |
Following up on #5236 (comment), the proposed solution doesn't cover all bases now that abstract classes are supported.
On a related note, interfaces and abstract classes feel a bit kludgey since I can't seem to verify that an arbitrary object actually conforms to them. I can require them as parameter types and use user-defined type guards, but there's no generic equivalent to
instanceof
.The text was updated successfully, but these errors were encountered: