-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Compiler should complain about implicit return value of child class constructor, if it does not satisfy child class type #13819
Comments
But the problem is not in |
Ah sry, that was not clear enough. The issue is not about the missing |
I think this was a problem that we understood, but because of the existing difficulties in modeling the issue and the relative rarity that we anticipated, we decided to be lax. I don't think I know of a specific mechanism to describe this behavior except for a new encoding on construct signatures. As a strawman, something like this: interface FooStatic {
new(): explicit return Foo
} But even this suffers the issue that it doesn't communicate whether or not the returned object's prototype was adjusted appropriately. |
CC @justinfagnani for any ideas |
But wouldn't be a compilation error sufficient enough? Since tsc already throws in this case: class Foo {
bar: string;
baz: number;
constructor() {
return {
bar: 'hey', // <-- baz is missing
};
}
} I would expect that the compiler also throws when the described issue occurs. |
I think the problem is that constructors are typed to return their containing classes instance type, and not the (ignore the runtime infinite loop in the constructor :) ) export class A {
constructor() {
return new A(); // no error
}
foo(): this {
return new A(); // error: Type 'A' is not assignable to type 'this'.
}
} If constructors had to return a export class A {
constructor() {
return Object.create(new.target.prototype);
}
} ^ I see that |
I wanted to add some more realistic use-cases for returning something other than 1: Singleton / cached instances. Here's some attempt to make singletons to work with subclassing, assuming subclasses cooperate by not double-initializing... const cache = new WeakMap();
export class A {
constructor() {
const cached = cache.get(new.target);
if (cached != null) {
return cached as this;
}
cache.set(new.target, this);
}
} 2: "Constructor call trick" this is what HTMLElement does internally to upgrade custom elements during parsing. This is basically working around the fact that you can't let instance: A|null;
export class A {
constructor() {
if (instance != null) {
const i = instance;
instance = null;
return i as this; // we know it's the right type
}
}
}
export type Constructor<T> = new(...args: any[]) => T;
// upgrades an existing object o to be of type constructor
export function upgrade<T extends Constructor<A>>(o: any, constructor: T): T {
instance = o;
Object.setPrototypeOf(o, constructor.prototype);
const upgraded = new constructor();
console.assert(o === upgraded);
return o;
} |
I see that 2.2rc supports |
@DanielRosenwasser do you think my diagnosis is sound here? Should the issue be changed to require a |
TypeScript Version: 2.1.5
Since TypeScript 2.1 (due to #7574) the return value of a constructor call is the return value of the super constructor. So that
is compiled to
Actual behavior:
The actual problem is, that the implementation of
Child
(see example below) will be compiled without errors, despite of the fact that the instance (child
) does not satisfy its typeChild
, becausesomeMethod
does not exist onchild
.Expected behavior:
The compiler should complain about the implicit return value of the child class constructor, if this value is not a type of the child class. So that either an explicitly defined return value for the child class constructor should be forced like...
or the return value has to be extended (
const base = super()
) to fulfill the typeChild
or all members of the child class have to be optional.The text was updated successfully, but these errors were encountered: