Open LPGhatguy opened 9 years ago
Also, for clarity, as discussed in #4356 it is logical based on the ES specification to strongly type .constructor
property of an instance and the following is essentially equivalent valid ways of creating instances:
class Foo {
foo() { console.log('bar'); }
}
let foo1 = new Foo();
let foo2 = new foo1.constructor();
Accepting PRs for this. Anyone interested? :smile:
I spoke with @ahejlsberg about this - we could type constructor fairly well just with a change to our lib.d.ts now that we have this
types (in theory):
interface Constructor<T> {
new (...args: any[]): T;
prototype: T;
}
interface Object {
constructor: Constructor<this>;
}
But! There are two issues - we don't instantiate this types on apparent type members (as in, anything on object) correctly right now, and there would be a performance impact in making every object have a this typed member (it's constructor member). Additionally, this method doesn't capture the arguments of the constructor of the class (or its static members), so it could stand to be improved.
Additionally, this method doesn't capture the arguments of the constructor of the class (or its static members),
This was a dealbreaker for us since it wouldn't even address the problem in the OP. Wiring it up in the compiler the same way we do prototype
seems necessary.
Can take a look, does
1) Removing from lib.d.ts:
interface Object {
constructor: Function;
2) inferring/adding a "constructor" property/symbol with a type to the classType seem like the right thing to do?
Nevermind, it's a bit more involved then I thought. Attempt here in case helpful: https://github.com/Microsoft/TypeScript/compare/master...jbondc:this.constructor
Think proper way involves changing ConstructorKeyword
in getSymbolAtLocation()
As I mentioned in #5933, will this not be a breaking change for assigning object literals to class types?
class Foo {
constructor(public prop: string) { }
}
var foo: Foo = { prop: "5" };
This is currently allowed, but with this issue fixed it should produce an error that the literal is missing the constructor
property? If it didn't produce an error, then it would be allowed to call statics on Foo such as foo.constructor.bar()
which would break if foo.constructor
wasn't actually Foo.
Would this be possible to implement solely with the polymorphic this
type that's a work in progress? Assuming typeof
mechanics would work there as well, could the signature for Object.prototype.constructor
be set to:
interface Object {
constructor: typeof this;
}
The general problem we face here is that lots of valid JavaScript code has derived constructors that aren't proper subtypes of their base constructors. In other words, the derived constructors "violate" the substitution principle on the static side of the class. For example:
class Base {
constructor() { } // No parameters
}
class Derived {
constructor(x: number, y: number) { } // x and y are required parameters
}
var factory: typeof Base = Derived; // Error, Derived constructor signature incompatible
var x = new factory();
If we were to add a strongly typed constructor
property to every class, then it would become an error to declare the Derived
class above (we'd get the same error for the constructor
property that we get for the factory
variable above). Effectively we'd require all derived classes to have constructors that are compatible with (i.e. substitutable for) the base constructor. That's simply not feasible, not to mention that it would be a massive breaking change.
In cases where you do want substitutability you can manually declare a "constructor"
property. There's an example here. I'm not sure we can do much better than that.
Well, since the constructor
, per spec (and after typescript compilation) is always the defined in the prototype, I don't see why it shouldn't be strongly typed. If people are hacking their prototypes manually, let them typecast their code. this.constructor
should be the class definition, and super.constructor
the immediate super prototype definition, and so on.
Since non-typescript modules usually have their manual typings (through DefinitelyTyped), it's safe to rewrite their constructor there as needed (in case the module author did something silly like):
function Foo(){
}
Foo.prototype = {
constructor: Foo,
someMethod: function() {
}
}
I don't understand what's going on in this thread. Need to take it back to the design meeting.
Animal.run()
this is code not use Cat.run
owerriden method. i need write this.constructor.run()
but it get compilation error.class Animal {
static run (){
alert('Animal run')
}
run(){
this.constructor.run() // compile error
// if i write Animal.run() this code not use Cat.run owerriden method
}
}
class Cat extends Animal {
static run (){
super.run()
alert('Cat jump')
}
}
let cat = new Cat()
cat.run()
add keyword static
for access to current static props, static
will be alias for this.constructor
in self methods, and alias for this
in static methods
and code will be:
class Animal {
static run (){
alert('Animal run')
}
run(){
static.run() // compile to this.constructor.run()
}
}
class Cat extends Animal {
static run (){
super.run()
alert('Cat jump')
}
}
let cat = new Cat()
cat.run() // alert('Animal run') alert('Cat jump')
keyword static
can be to used in the next standards ES, and may have different semantics
There is nothing wrong with adding a little sugar. This really is not anything bad. All restrictions that you come up with, to serve for the good of the people. And in this case, the failure to implement the keyword static
, just from the fact that it is not in the standard, does more harm than good.
Language is a tool, and, as we understand, should be the keyword static
. And we all know that.
TypeScript is a ES with types. ES not has
static
keyword, and we must not it have
This is nonsense and is an example of how not built right ideology hinders the improvement of living standards of people. The rule for the rule.
We need to add the keyword, since it will allow programmers to better to program. That's the purpose of the language. Am I wrong? Am I wrong???
static.run()
isn't valid javascript, in all senses. using an imaginary keyword that looks like a global. the Class specification does have static
as a keyword http://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions
one thing is transpiling code that works in ES5, another thing is changing the input to a completely different output. await / async for example are in the spec, and the generated code just makes you able to use ES2016 today. the coffeescript dark days are now past at last.
Typescript does one thing and does really well: having plain JS strongly-typed.
they just need to fix the constructor to be strongly typed as well. I have no problem doing this.constructor.some()
since using ClassDefinition.some()
is awkward and makes inheritance a (even more) pain.
Has anyone figured out some kind of workaround for this yet?
Maybe use new keywords is better. For example:
In TypeScript:
class Foo {
static base() {
return 'FOO'
}
static foo() {
return self.base()
}
static bar() {
return static.base()
}
cfoo() {
return self.base()
}
cbar() {
return static.base()
}
}
class Bar extends Foo {
static base() {
return 'BAR'
}
static more() {
return parent.base()
}
cmore() {
return parent.base()
}
}
Expect executing result as follow:
Foo.base() // 'FOO'
Foo.foo() // 'FOO'
Foo.bar() // 'FOO'
Bar.base() // 'BAR'
Bar.foo() // 'FOO'
Bar.bar() // 'BAR'
Bar.more() // 'FOO'
const foo = new Foo()
foo.cfoo() // 'FOO'
foo.cbar() // 'FOO'
const bar = new Bar()
bar.cfoo() // 'FOO'
bar.cbar() // 'BAR'
bar.cmore() // 'FOO'
The three keywords self
, static
, parent
means different access controls as follow:
self
: access the constructor where the keyword is strictlystatic
: dynamic access the constructor according to the access contextparent
: access the property of the super constructor of the constructor where the keyword isThe compile result is(omit useless codes):
(function (_super) {
function _ctor() {}
_ctor.foo = function() {
_super.bar() // from parent.bar()
_ctor.bar() // from self.bar()
this.bar() // from static.bar()
}
_ctor.prototype.foo() {
_super.bar() // from parent.bar()
_ctor.bar() // from self.bar()
this.constructor.bar() // from static.bar()
}
})(function _super() {})
@acrazing as already discussed in this thread that is a bad idea and it is contrary to the design goals of TypeScript.
To understand why it is a bad idea, consider that static
already has a meaning in ECMAScript and that this meaning may expand. What if TC39 adds support for precisely this feature? What if it adds the same syntax with a different meaning? What is static
is endowed with a meta-property like new
was with target
?TypeScript, as a JavaScript superset, would need to implement the new behavior of static
. This would break existing code.
That is one reason to not add new syntax in value positions. It breaks compatibility.
Possible workaround:
class Foo {
'constructor': typeof Foo; // <-- this line here
static method() { return 1 }
}
const foo = new Foo();
foo.constructor.method(); // Works
new foo.constructor(); // Works
Having an explicit opt-in isn't necessarily a bad thing either, since it makes it obvious that you're going to be using the .constructor
property for something.
It seems a bit odd that the quotes are required, but just having constructor: typeof Foo
will give you:
[ts] Constructor implementation is missing.
if you have no constructor implementation, or
[ts] '(' expected.
if you do.
As a bonus, you can use this to implement static properties on interfaces:
interface I {
constructor: {
staticMethod(): number;
}
instanceMethod(): string;
}
class C implements I {
'constructor': typeof C;
static staticMethod() { return 1 }
instanceMethod() { return '' }
}
If someone wants to make it work with prettier (which removes the quotes), wrapping around []
does the trick:
class C {
['constructor']: typeof C;
}
@ahejlsberg
Here we just wanna to use this.constructor
to retrieve static member.
If it must add "constructor": typeof XXX
to every class, it also seems too unnecessary.
Since this.constructor
have some unavoidable type problem, can we add a new grammer for this.
For example: this::staticMember
compiles to this.constructor.staticMember
.
Treat type of this::XXX
as (typeof typeof this)['XXX']
Just like it in PHP: self::staticMember
.
It also use ::
in C++
.
I agree with @k8w as introducing a new concept/keyword like constructof
would be verbose!
As for the constructor.prototype
, it cannot be strongly typed as may developers still use that means to add properties. Moreover, have you notice that though not suggested in autocomplete, when you do use it, no error is triggered?
Any progress on this?
I'm trying to call a static property from a Sequelize model as such:
const ModelT = options.target().constructor as typeof Model<T>;
return ModelT.count({ where });
options.target()
return type is new () => T
and T extends Model<T>
count
is a static method of Model<T>
, but I'm unable to type that cast properly. Any help would be appreciated.
Looking at this now, having a built in polymorphic definition of this.constructor would probably do the trick. The biggest issue I have with this:
class Foo {
'constructor': typeof Foo; // <-- this line here
static method() { return 1 }
}
const foo = new Foo();
foo.constructor.method(); // Works
new foo.constructor(); // Works
Is that it's not polymorphic. For example the above falls apart when extending classes.
class Foo {
'constructor': typeof Foo; // <-- this line here
static method() { return 1 }
}
class FooChild extends Foo {
}
const foo = new Foo();
const fooChild = new FooChild();
foo.constructor.method(); // Works
new foo.constructor(); // Works
fooChild.constructor.method(); // Doesn't Work
new fooChild.constructor(); // Doesn't Work
Relevant ECMAScript proposal: https://github.com/rbuckton/proposal-class-access-expressions
It's true what @ahejlsberg's comment here about type incompatibility which will happen if every object has different types of constructor
property.
However, after thinking for a while, I think we were providing a little less information than we could provide. Well, I will explain my idea for this issue.
constructor
type information from the new
operatortype Instance<T, P> = T & { constructor: P }
Instance<T, P>
augments T
type with the type information of constructor
property. We can provide the information somewhere and I think the sweet spot is at the return value of the new
operator. Look at the example below. (open in TypeScript playground here)
class Class1 {
constructor(value: number) {
}
}
class Class2 extends Class1 {
constructor(value: string) {
super(Number(value))
}
}
let i1 = new Class1(100) as Instance<Class1, typeof Class1>
let i2 = new Class2("1") as Instance<Class2, typeof Class2>
// ^ this is what I propose: make this auto-magically happens
i1 = i2 // <-- error because incompatible constructors
let di1: Class1 = i1 // <-- let's reduce information that we have here
let di2: Class2 = i2
di1 = di2 // <-- not even an error happens here :)
This idea will solve the problem here but still allows for more specific typing if needed.
The behavior of returning T
from new
operator has been around there for a long time. I'm thinking about automagically reduce the information at assignment operation if the variable type not explicitly stated.
let s1 = new Class1(100) // <-- ReducibleInstance<Class1, typeof Class1>
let s2 = new Class2("1") // <-- ReducibleInstance<Class2, typeof Class2>
// s1.constructor. <-- resolve to typeof Class1
s1 = s2 // <-- reduce s1 to Class1 before assignment only if signature is incompatible
// s2 is still typed as ReducibleInstance<Class2, typeof Class2>
// s1.constructor. <-- now resolve to Function
Well, I'm not finished yet. This alone is not enough to solve the whole problem. Therefore, I'll propose another design idea.
typeof this
typeI have an idea to create a new type named typeof this
type which is similar to polymorphic this
type but for the type of the class instead of for the instance. This is similar to @LPGhatguy comment here. I'll highlight some traits of my concept idea below.
typeof T
from the outsideLike polymorphic this
type which dissolves to T
when accessed from the outside, polymorphic typeof this
type dissolves to typeof T
when accessed from the outside.
class Class3 {
method1(): this { // <-- type: this
return this
}
method2(): typeof this { // <-- type: typeof this
return this.constructor
}
}
let t1 = new Class3()
t1.method1() // <-- type: Class3
t1.method2() // <-- type: typeof Class3
Like polymorphic this
type which resolves currently available members when accessed from the inside, polymorphic typeof this
resolves currently available static members of the class.
class Class4 {
static staticProp1: number
prop1: number
method1() {
let obj = this // <-- type: this
// obj. <-- resolve prop1, method1
let cls = this.constructor // <-- type: typeof this
// cls. <-- resolve to staticProp1, etc
}
}
class Class5 extends Class4 {
static staticProp2: number
prop2: number
method2() {
let obj = this // <-- type: this
// obj. <-- resolve prop1, prop2, method1, method2
let cls = this.constructor // <-- type: typeof this
// cls. <-- resolve to staticProp1, staticProp2, etc
}
}
Reason: child's construct signature is allowed to be incompatible with parent's construct signature. We don't have enough information to figure out the signature of the final constructor at the moment.
class Class6 {
constructor(readonly value: number) {
}
method1() {
new this.constructor(17) // <-- Error TS2351: Cannot use 'new' with an ...
}
}
class Class7 extends Class6 {
constructor(value: string) {
if (typeof value !== "string") {
throw new Error("value must be a string")
}
super(Number(value))
}
// method1() {
// new this.constructor(17) <-- this expr. will break the rule if allowed
// }
}
this
typeSince we're not changing the type of Object.prototype.constructor
, we have to provide access to this feature. I'm thinking about putting this on this
type.
class Class8 {
method1() {
// this.constructor. <-- resolve to typeof this
}
}
let it1: Class8 = new Class8()
// it1.constructor. <-- resolve to Function
My actual problem is I want to create an extendable class that leverages immutable fluent interface pattern. I took an example from TypeScript documentation here with a few changes. (open in playground)
class Calculator {
constructor(readonly value = 0) {
}
protected newInstance(value: number): this {
return new (this.constructor as any)(value) // <-- not type-safe
}
add(operand: number) {
return this.newInstance(this.value + operand)
}
// ... other operations go here
}
let v = new Calculator(2)
.add(1)
.value
One of my objectives is I want to make this code more type-safe since the constructor's type signature is not checked yet.
It will be possible to resolve to resolve static members of the class from this.constructor
attribute and to do type-checking on the constructor of the child class from the parent class using this idea by modifying the Calculator
class above to be like this.
class Calculator {
// ...
protected newInstance(
this: this & { constructor: { new(value: number) } },
value: number
): this {
return new this.constructor(value)
}
add(
this: this & { constructor: { new(value: number) } },
operand: number
) {
return this.newInstance(this.value + operand)
}
// ... other operations go here
}
Every method which associates with the fluent interface will not be able to be called from an instance of the class which doesn't have the required signature.
class BrokenCalculator extends Calculator {
constructor(value: string) {
super()
}
}
let v2 = new BrokenCalculator()
.add(5) // <-- this resolves to:
// ReducibleInstance<BrokenCalculator, typeof BrokenCalculator>
// (error: not compatible with required this by the method)
Thank you. I'm looking forward to hearing about this. I'm sorry for long post here.
I hope this idea will be able to solve our problems. 😃
It's quite boring to write ['constructor']: typeof FooClass
every time so I think it worth to fix this.
Solution must be simple. The following code placed inside of the class constructor or its method
...
this.constructor
...
is always pointing to the class' variable unless someone failed to assign Foo.prototype.constructor =
property by Parent
class during inheritance.
Any news on this?
This would be nice to have! It's a very common case. There are often libs that rely on settings being specified on classes (and extended in subclasses), and those libs retrieve the settings via this.constructor.someSetting
.
@ahejlsberg
The general problem we face here is that lots of valid JavaScript code has derived constructors that aren't proper subtypes of their base constructors. In other words, the derived constructors "violate" the substitution principle on the static side of the class.
That's true, but has no bearing on the fact that when we read this.constructor
we intuitively expect a value of the type of this's constructor assuming it was defined with we don't know which subclass class
. That case can be satisfied.this
will be made from.
Now, what we do with the constructor after we've read its value is a different story. Maybe there needs to be two type checks: one prevents it from being substitutable when called (typed as Function
), and the other just treats it as a static access when reading properties. Calling new this.constructor
is not usual, but reading this.constructor.foo
is common.
I was OK with adding field 'constructor': typeof ClassName
but after upgrading to TS 3.5.1 I receive error Classes may not have a field named 'constructor'
. It is really obtrusive that this issue is open for 4 years :)
@ayZagen That error message was added in #31119 and relates to #31020 There seems to be an allowance for the version with brackets:
class A {
['constructor']!: typeof A // ! to avoid 'use before init' error in constructor
static methodA() { }
constructor() { this.constructor.methodA(); }
}
new A().constructor.methodA();
You can also use interface merging, which avoids both []
and !
interface C {
constructor: typeof C
}
class C {
static methodC() { }
constructor() { this.constructor.methodC(); }
}
new C().constructor.methodC();
I can't remember if this was always the case, but it works with subclasses now too
class B extends A {
['constructor']: typeof B
static methodB() { }
}
new B().constructor.methodA();
new B().constructor.methodB();
Most people want to read this.constructor.someProp
, they hardly ever want to call new this.constructor(...)
. Maybe the type checker needs to do two things:
this.constructor
is being called as new this.constructor(...)
, then in this case it should fail with a type error because of the substitutability problem (i.e. treat it as Function
in this case, because we don't know what a subclass constructor signature will be (unless of course someone forces it to be what they want with the form ['constructor']!: typeof SomeClass
in the subclass)).this.constructor.someProp
, treat is as a static access assuming because the class shape is enforced by TypeScript, don't treat it as Function
in this case. So perhaps if this.constructor.foo
happens in the scope of class B, then treat this.constructor
as typeof B
.This would prevent us from having to write ['constructor']!: typeof B
in every subclass to have proper static inheritance.
The fact that the following works,
class A {
['constructor']!: typeof A
static methodA() { }
constructor() { this.constructor.methodA(); }
}
new A().constructor.methodA();
is weird because in JavaScript it is an error. Try it in Chrome console, without the types:
class A {
['constructor']
static methodA() { }
constructor() { this.constructor.methodA(); }
}
new A().constructor.methodA();
If I recall correctly, one of the goals of TypeScript is that types can be stripped to leave valid JavaScript, but that's not true in this case.
Looking forward to an official solution!
@trusktr that's not valid JavaScript and it's also not what TypeScript compiles to. This is what TypeScript compiles to:
class A {
static methodA() { }
constructor() { this.constructor.methodA(); }
}
new A().constructor.methodA();
that's not valid JavaScript
@alfaproject Thanks. I already knew that. That's what I was pointing out: the code in TypeScript is invalid JavaScript (with types), and I know it converts to valid JS after compile.
I'm just saying that it is strange and not obvious what the behavior should be. If I go by my JS knowledge (coming from JS to TS), then I'd be wrong: I would have never thought to define the same property twice like this (because it is invalid JavaScript).
Do you get what I mean? I'm just saying, that an official solution will be nice (and won't involve invalid JavaScript inside of TypeScript).
I use this way for inherit class
export function bindConstructor<T>() {
return <U extends T>(constructor: U) => {
// noinspection BadExpressionStatementJS
constructor;
};
}
export interface BaseClass<T extends typeof BaseClass = typeof BaseClass> {
constructor: T;
}
export interface BaseClassConstructor<T = {}> {
new (...items: any[]): T;
}
export interface AnyConstructor<T = {}> extends BaseClassConstructor {
new (...items: any[]): T;
}
@bindConstructor<BaseClassConstructor>()
export class BaseClass<T extends typeof BaseClass = typeof BaseClass> {
constructor(...items: any[]) {}
}
interface ChildInterface {
readonly Zero: number;
}
interface ChildConstructor<T = {}> extends AnyConstructor {
new (...items: any[]): T;
Two: number;
}
@bindConstructor<ChildConstructor>()
class Child<T extends typeof Child = typeof Child>
extends BaseClass<T & AnyConstructor>
implements ChildInterface {
readonly Zero = 0;
protected readonly One = 1;
static Two = 2;
protected static Three = 2;
}
@bindConstructor<ChildConstructor>()
class Son<T extends typeof Son = typeof Son> extends Child<T & AnyConstructor>
implements ChildInterface {
constructor(...items: any[]) {
super(...arguments);
console.log(this.Zero);
console.log(this.One);
console.log(this.constructor.Two);
console.log(this.constructor.Three);
}
}
another way: ps: override type() without return type
export function bindConstructor<T>() {
return <U extends T>(constructor: U) => {
// noinspection BadExpressionStatementJS
constructor;
};
}
export type BaseType<U> = InstanceType<U & { new (...args: any): any }>;
export interface AnyConstructor<U extends {} = {}> {
new (...items: any[]): U;
}
//export interface BaseConstructor<U extends typeof Base> extends AnyConstructor<U> {
// create(...items: any[]): BaseType<U>;
//
// new (...items: any[]): BaseType<U>;
//}
export abstract class Base {
constructor(...items: any[]) {
this.setup();
}
static create<U extends typeof Base>(this: U, ...items: any[]): BaseType<U> {
return Reflect.construct(this, items);
}
setup(): void {}
//noinspection JSMethodCanBeStatic
type() {
return Base;
}
}
interface YI {}
interface YC<U extends YI = YI> extends AnyConstructor<U> {
z(): string;
}
abstract class X extends Base implements YI {
xa!: string;
xf!: string;
xfr!: string;
protected ef: string = 'ex';
protected readonly efr: string = 'ex';
protected get ea() {
return 'ex';
}
type() {
return X;
}
}
@bindConstructor<YC>()
class Y extends X {
protected ef: string = 'ey';
protected readonly efr: string = 'ey';
protected get ea() {
return 'ey';
}
static z() {
return 'z';
}
setup(): void {
this.xf = 'ey' === this.ef ? 'TRUE' : 'FALSE';
this.xfr = 'ey' === this.efr ? 'TRUE' : 'FALSE';
this.xa = 'ey' === this.ea ? 'TRUE' : 'FALSE';
}
type() {
return Y;
}
}
Any hope for this feature? A long awaited "ergonomic" improvement.
I use this workaround to call a static method inside a class :
class A {
constructor() { }
static a(): void {
console.log("from a static method");
}
b(): void {
this.constructor['a'](); // <= call the a() static method
}
}
(new A()).b();
And this workdaround to retrieve a static property inside a class :
class A {
public static a: string = "hello world";
constructor() { }
static b(): void {
console.log(this.a + " from b"); // <= from static method
}
c(): void {
console.log(this.constructor['a'] + " from c"); // <= from non-static method
this.constructor['b']();
}
}
(new A()).c();
VSCode
Chrome Console
I'm currently trying to do some type checking (anything really) to see an object extends a class. One notable difference between classes and objects is the list of attributes on their constructor.
The following would be a functional way to tell if something was a class or not:
T extends {constructor: { defineProperty: any}} ? ProbablyNotAClass : ProbablyAClass
One thing that no one seems to have called out during this discussion is that there's a difference between T.constructor
and T.prototype.constructor
. T.constructor
type is already accurate - it is the constructor function and not the class object, so it lacks things like static members. T.prototype.constructor
is indeed just a reference to the class object, and this is where we have this type problem. Try this on TS Playground:
class Klass {
static prop = 1
}
console.log(Klass.constructor, (Klass.constructor as any).prop)
console.log(Klass.prototype.constructor, (Klass.prototype.constructor as any).prop)
T.constructor
is the global Function
object for classes (and functions in general), so T.prototype.constructor !== T.constructor
, because T !== Function
.
@ExE-Boss oops, you're right; i deleted my comment.
Finally this is best solution eval('this.constructor.staticFn()')
@Lugeen Then what's the reason you use TypeScript? Why don't you just use plain JavaScript?
eval('this.constructor.staticFn()')
Isn't this better?
// @ts-ignore
this.constructor.staticFn()
@trusktr thank you , your answer is perfect solution .
I thought this can be implement if there's exact type
Every time I type (this.constructor as typeof MyClass).someStaticMethod()
I scold the TypeScript with the last words. Optimized for developers stress?
I'd like to share my workaround, for referring static members in a convenient way. But the newable signature is not working well.
type Constructor = new (...args: any[]) => any;
function TypedConstructor<S extends void | Constructor = void>(Super?: S):
S extends Constructor
? {
[K in keyof S]: S[K]
} & {
new <U>(...args: ConstructorParameters<S>): InstanceType<S> & { constructor: U }
}
: {
new <U>(): { constructor: U }
}
{
return Super || Object as any;
}
// for base class, use TypedConstructor()<typeof [ThisName]>
class A extends TypedConstructor()<typeof A> {
static staticA() { return 0; }
protected static protected_staticA() { return 1; }
constructor(x: number) {
if (typeof x !== 'number') {
throw new Error('Argument type error!')
}
super();
}
foo() {
console.log(this.constructor.staticA()) // OK
console.log(this.constructor.protected_staticA()) // OK
}
}
// for inherited class, use TypedConstructor(Super)<typeof [ThisName]>
class B extends TypedConstructor(A)<typeof B> {
static staticB() { return 'bbb'; }
protected static protected_staticB() { return 'xxx'; }
constructor(x: string) {
if (typeof x !== 'string') {
throw new Error('Argument type error!')
}
super(Number(x));
}
foo() {
super.foo();
}
bar() {
console.log(this.constructor.staticA()); // OK
console.log(this.constructor.protected_staticA()) // OK
console.log(this.constructor.staticB()); // OK
console.log(this.constructor.protected_staticB()); // OK
}
}
var a = new A(0);
var b = new B('1');
b.bar();
// Do not use `new` with `constructor`!
var c = new b.constructor(0) // Compiled, but will throw runtime error.
Given
The current type of
Example.constructor
isFunction
, but I feel that it should betypeof Example
instead. The use case for this is as follows:I'd like to reference the current value of an overridden static property on the current class.
In TypeScript v1.5-beta, doing this requires:
After this proposal, the above block could be shortened to:
This removes a cast to the current class.