Skip to content

Constructor in interfaces throws error #8917

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

Closed
DaSchTour opened this issue Jun 1, 2016 · 10 comments
Closed

Constructor in interfaces throws error #8917

DaSchTour opened this issue Jun 1, 2016 · 10 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@DaSchTour
Copy link

DaSchTour commented Jun 1, 2016

TypeScript Version:

1.8.10

Code

Taken from http://www.typescriptlang.org/docs/handbook/interfaces.html
Just changed the return to omit errors by implicit any.

interface ClockConstructor {
    new (hour: number, minute: number): ClockConstructor;
}

class Clock implements ClockConstructor {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

Expected behavior:

Clock correctly implements ClockConstructor

Actual behavior:

Error:(5, 7) TS2420: Class 'Clock' incorrectly implements interface 'ClockConstructor'.
Type 'Clock' provides no match for the signature 'new (hour: number, minute: number): ClockConstructor'

@ahejlsberg
Copy link
Member

That example is meant to illustrate an error, as is mentioned in the surrounding handbook text. The error is actually what is expected.

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jun 1, 2016
@DaSchTour
Copy link
Author

Okay, so to summarize. There is no possibility to create a full Interface for a class with a constructor?

@ahejlsberg
Copy link
Member

Yes, there is, but it ends up being two interfaces, one for the static side and one for the instance side:

interface ClockConstructor {
    new (hour: number, minute: number): ClockInstance;
}

interface ClockInstance {
    currentTime: Date;
}

class Clock implements ClockInstance {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

var cc: ClockConstructor = Clock;
var ci = new cc(9, 15);
var t = ci.currentTime;

@DaSchTour
Copy link
Author

I guess it couldn't be made more complicated.

@kitsonk
Copy link
Contributor

kitsonk commented Jun 2, 2016

It is only logical. A JavaScript class is essentially a constructor function with a prototype. There is the interface to the constructor function, along with any "static" methods and properties, and then there is the interface to the prototype, which the constructor function creates a new instance of when the function is called with the new keyword.

TypeScript typically abstracts you from that complexity when you use the class keyword. But the complexity is trying to describe things in the underlying language (ECMAScript), not some arbitrary complexity in TypeScript.

Some would question that creating a whole interface for a class couldn't be more complicated. What is the use case for creating it, when even in definition files, classes can be described as a single interface using the class keyword?

@DaSchTour
Copy link
Author

DaSchTour commented Jun 2, 2016

Well, the most ugly point about this is, that I have to create an empty interface for the empty static part of my class. And my IDE also tells me, that the Constructor Interface is not used. What sounds only logic to me, because there is no implementation reference for the constructor interface.

@kitsonk
Copy link
Contributor

kitsonk commented Jun 2, 2016

Well, the most ugly point about this is, that I have to create an empty interface for the empty static part of my class.

Again, why are you creating interfaces for constructors? I think you are mis-understanding something. You asked how to do it (which was explained) but you have articulated what your use case is for doing it. As mentioned, above, in definition files (.d.ts) class is a special keyword that automatically expresses both those interfaces and do not require an implementation.

@DaSchTour
Copy link
Author

I'm creating the interface for a constructor, so I can pass the class reference in an configuration object. The configuration can point to different classes that all extend the same abstract class which implements the interface. To make compile work with this I need an interface for the constructor to use the interface instead of the abstract class as the type reference.

@kitsonk
Copy link
Contributor

kitsonk commented Jun 2, 2016

When used as a type, the keyword typeof refers to the interface of the constructor instead of the instances created. For example:

class Foo {
    foo: string = 'foo';
}

function onlyFooConstructor(Ctor: typeof Foo) {
    console.log(Ctor);
}

onlyFooConstructor(Foo);

const foo = new Foo();

onlyFooConstructor(foo); // Errors

class Bar extends Foo {
    bar: string = 'bar';
}

onlyFooConstructor(Bar);

class Baz {
    baz: string = 'baz';
}

onlyFooConstructor(Baz); // Errors

But remember, TypeScript is a structural typing language, versus nominal, so if the constructor is the same shape (e.g. has matching static methods and generates an object that is assignable to the prototype of the constructor) it will pass the guard.

@RyanCavanaugh
Copy link
Member

pjustino pushed a commit to pjustino/typescript-book that referenced this issue Aug 21, 2017
I notice that the interface crazy could not be implemented in a class (please check here: microsoft/TypeScript#8917) and I made a fix with some text to explain better the pattern.
I also fixed the constructor to not return any value as it seems not to be possible. Please check here: microsoft/TypeScript#11588
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants