Reputation: 859
Suppose I have this inheritance tree, with a static sample function that exists in the classes D.ts
, E.ts
, F.ts
, G.ts
:
I want the thisIsTestFunction ()
to be a static function and not an abstract function, I don't want this function appear in the bases classes (A, B, C)!
I created an array and I want to run the array in a loop and call the static function:
let objects : A = new Array(20);
for (const object of objects ) {
// object.thisIsTestFunction(); ??
// How call the function here ?
}
I try to call the function with object.constructor.name
but it doesn't working.
I know that a static function can only be called using the class name, But abstract function is not a good solution for me, I want the function to appear only once in each class and be a static function.
Upvotes: 1
Views: 212
Reputation: 329783
The main issue that prevents the compiler from understanding what you're doing is that the constructor
property of an object is not strongly typed. See microsoft/TypeScript#3851 for more information. Right now, object.constructor
is just typed as Function
, which isn't known to have a thisisTestFunction()
method. One thing you can do is manually give every concrete class an appropriate strongly-typed constructor
property, but this is tedious (and has some gotchas about subclassing that I won't go into here):
class D extends C {
public static thisisTestFunction() {
alert("Testing");
}
["constructor"]: typeof D // here
}
And then if you had an object of, say, type D
you could do this:
const d = new D();
d.constructor.thisisTestFunction(); // okay
Another problem is that with an array of objects of type A
, the compiler has no way to know that each element will definitely be one of D
, E
, F
, or G
. TypeScript has no concept of a final
class (see microsoft/TypeScript#5863), and even if it did it wouldn't necessarily be able to represent that the A
hierarchy is "sealed":
class Oops extends A {
["constructor"]: typeof Oops;
// no static function here
}
let someObjects: A[] = new Array(20);
someObjects.push(new Oops()); // what if the array contains an Oops?
for (const object of someObjects) {
object.constructor.thisisTestFunction(); // still error
}
Instead, you might want to tell the compiler that your array contains elements not of type A
, but of one of the particular concrete subclasses of A
that you care about:
type ConcreteA = D | E | F | G;
let objects: ConcreteA[] = new Array(20);
objects.push(new Oops()); // error now to do this
for (const object of objects) {
object.constructor.thisisTestFunction(); // okay
}
The ConcreteA
type is the union of D
, E
, F
, and G
, and as long as each one of those has a strongly-typed constructor
that knows about the thisisTestFunction()
static method, then it will work without error.
As an aside, if the implementation of thisisTestFunction()
is truly identical in each concrete subclass, you might consider putting that static function up in A
anyway, and mark the method as requiring that its this
context (as represented by a this
parameter) correspond to a concrete constructor:
abstract class A {
public static thisisTestFunction<T extends new (...args: any) => A>(this: T) {
alert("Testing");
}
}
This is less code, and TypeScript will complain if you try to call it on A
, B
, or C
, but allow it on D
, through G
:
A.thisisTestFunction(); // error!
B.thisisTestFunction(); // error!
D.thisisTestFunction(); // okay
Okay, hope that helps; good luck!
Upvotes: 2