Skip to content
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

Can't typecheck constructor in class generic parameter #23264

Closed
albyrock87 opened this issue Apr 9, 2018 · 8 comments
Closed

Can't typecheck constructor in class generic parameter #23264

albyrock87 opened this issue Apr 9, 2018 · 8 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@albyrock87
Copy link

TypeScript Version: 2.8.1

Search Terms: generics constructor type check constraint new

Code

export interface HasOneParameterConstructor<T> {
  new (p1: any): T;
}

class Foo {
  constructor(p1: string) {

  };
}

class Bar<T extends HasOneParameterConstructor<T>> {

}

class BarFooImpl extends Bar<Foo> { // error here under "Foo"

}

Expected behavior:
No errors: constructor type checking works

Actual behavior:
error TS2344: Type 'Foo' does not satisfy the constraint 'HasOneParameterConstructor'.
Type 'Foo' provides no match for the signature 'new (p1: any): Foo'.

Playground Link: Playground Test

Related Issues:
Maybe it's related to: Can't extend mixin constructor that constructs a generic type

@albyrock87 albyrock87 changed the title Can't typecheck constructor in generic parameter Can't typecheck constructor in class generic parameter Apr 9, 2018
@ghost
Copy link

ghost commented Apr 9, 2018

The problem is that an Foo is not HasOneParameterConstructor because you can't call new on an instance of Foo. You need to have separate types for the instance and class type.

class Bar<Instance, Class extends HasOneParameterConstructor<Instance>> {}
class BarFooImpl extends Bar<Foo, typeof Foo> {}

@ghost ghost added the Question An issue which isn't directly actionable in code label Apr 9, 2018
@albyrock87
Copy link
Author

Hi @andy-ms , I tried your suggestion but the problem persists:

export interface HasOneParameterConstructor<T> {
  new (p1: any): T;
}

export abstract class ConstructorCheck<T, Class extends HasOneParameterConstructor<T>> {
  
}

export abstract class MyBase<TImplementor> extends ConstructorCheck<TImplementor, typeof TImplementor> {

}

class BaseImpl extends MyBase<BaseImpl> {

}

Your example does work but it's hackable:

class Hack { constructor(a: string) {} }
class BarFooImpl extends Bar<Foo, typeof Hack> {}

I'm trying to create a library with some usage restriction, and that's not good.

@ghost
Copy link

ghost commented Apr 10, 2018

Note that TypeScript does not have any abstract static methods (#14600). It might be more helpful to determine what you're trying to accomplish -- currently Class is unused there. Note that the type parameter is only available as a type, and not as a value, so you won't be able to write new T inside of your class body, and as you've noticed typeof T doesn't work.

@albyrock87
Copy link
Author

Basically I want to stop the developer to define a new constructor for BaseImpl. This way I can create new Instances of BaseImpl using exactly the MyBase constructor.

@ghost
Copy link

ghost commented Apr 16, 2018

If I understand that right, you want something like this?

abstract class A {
	final constructor() {}
}

class B extends A {
	constructor() { super(); someBadThing(); } // Compile error, super constructor is final
}

It doesn't seem that useful to have an abstract class whose constructor can't be overridden -- sublcass constructors are forced to call the superclass constructor anyway. And one can of course do new B(); b.someBadThing(); even if the constructor isn't overridden.

@albyrock87
Copy link
Author

A final constructor would do the trick.
I'm in the Angular world, and I'm trying to stop the developer to @Inject other things in the B class.

@ghost
Copy link

ghost commented Apr 16, 2018

This is a duplicate of #1534 then -- I don't think there's a good way to do what you want right now. (You can't just make the constructor private because then the class can't be overridden at all.) It's possible that you could add a run-time check in A's constructor that this.constructor has no decorators? I don't really know how angular works though.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

2 participants