class MyClass{
someMethod(): MyClass{
return new MyClass();
}
}
How to reference current class, without explicitly passing the name?
Something like this:
class MyClass{
someMethod(): self{
return new self();
}
}
Obviously that doesn't work, but you get the idea.
TypeScript will not recognize this.constructor as callable. Use Object.getPrototypeOf(this).constructor to get a reference to it instead.
This is the most straightforward way of doing this with strict type safety:
class SelfReference {
/**
* Assign some constructor arguments to the instance for testing.
*/
constructor(private a: number, private b: number) {}
/**
* Create a new instance of `Object.prototypeOf(this).constructor`.
*/
newInstance(...args: ConstructorParameters<typeof SelfReference>) {
const { constructor } = Object.getPrototypeOf(this);
return new constructor(...args);
}
}
const firstReference = new SelfReference(1, 2);
const newReference = firstReference.newInstance(3, 4);
console.log({ firstReference, newReference });
Logs:
[LOG]: {
"firstReference": {
"a": 1,
"b": 2
},
"newReference": {
"a": 3,
"b": 4
}
}
I would not recommend doing this though, it's a pretty ugly anti-pattern.
TypeScript Playground
This can be accomplished by using getPrototypeOf:
Class myclass {
constructor() {}
class(): this {
const ctor = Object.getPrototypeOf(this).constructor;
return new ctor();
}
}
Using the ctor(), we are not calling myclass specifically!
Mozilla - Globa_Objects/Object/getPrototypeOf
class MyClass {
returnClass(): MyClass {
return new (this.constructor as typeof MyClass)();
}
}
const x: MyClass = new MyClass();
const newClass: MyClass = x.returnClass();
this is what you want !
class Foo {
foo(): this {
return this;
}
}
class Bar extends Foo { }
const bar: Bar = new Bar().foo();
Playground
Does this do what you want? You can explicitly grab the current constructor to make a "new this".
class MyClass{
someMethod(){
return new this.constructor();
}
}
let x = new MyClass();
let y = x.someMethod();
Here is a TypeScript solution.
There doesn't seem to be a way to use the this keyword in the function signature of a static method or the this.constructor keyword in the function signature of an instance method, both which would be equivalent to the Self keyword in other languages. However, as the this/this.constructor keyword would represent an instance of the Foo class, and an instance of a class which extends from the Foo class can be returned from a function with return type Foo, the return type of the function doesn't have to be a special keyword, but can just be the class name.
class Foo {
static createNewClass(): Foo {
return new this();
}
name = "Foo";
createNewClass(): Foo {
return new (this.constructor as typeof Foo)();
}
}
class Bar extends Foo {
name = "Bar";
}
const foo = new Foo();
const fooFromStaticMethod = Foo.createNewClass();
const fooFromMethod = foo.createNewClass();
const bar = new Bar();
const barFromStaticMethod = Bar.createNewClass();
const barFromMethod = bar.createNewClass();
console.log(fooFromStaticMethod.name, fooFromMethod.name, barFromStaticMethod.name, barFromMethod.name);
Playground
Related
I'd like to add a method to a method:
class MyClass {
public foo(text: string): string {
return text + ' FOO!'
}
// Some magical code which adds the method `bar` to `foo`.
}
const obj = new MyClass();
console.log(obj.foo('thing'));
console.log(obj.foo.bar('other thing'));
Is this possible? I've found a similar case for functions:
function Sum(x: number) { /*...*/ }
namespace Sum {
export function empty() { /*...*/ }
}
Is there a way to do the same with methods of a class?
I'd like have the code in the class, not monkeypatched after the object has been created.
You can access the function on the classes prototype and do whatever you like with it. I don't think it's especially pretty, but you can do something like this:
class MyClass {
constructor(myName) {
this.myName = myName
this.constructor.prototype.foo.bar = (function(a) {
console.log(this.myName, "calling foo.bar with:", a)
}).bind(this)
}
foo(text) {
return text + ' FOO!'
}
}
const obj = new MyClass("Mark");
obj.foo.bar('thing')
console.log(obj.foo('test'))
Not exactly what you're asking for, but the end result works:
class MyClass {
public foo: MyChildClass = new MyChildClass();
}
class MyChildClass {
public bar(text: string): string{
return text;
}
}
const obj = new MyClass();
console.log(obj.foo.bar('thing'));
EDIT I read your answer to my comment. I think the simpler way to achieve your goal is to use default parameters like such:
function foo(text: string, snapshot = false): string{
if(snapshot){
return 'snapshot';
}
return text;
}
console.debug(foo('test'));
console.debug(foo('test', true));
Now your solution has the advantage that you can see at the call site that you are requesting a bypass or a snapshot clearly because of the additional function names. You can achieve a similar result in typescript by replacing the arguments of foo with an interface with optional properties. In other languages, we would call this technique named parameters:
interface iFooArgs{
text: string;
bypass?: boolean;
snapshot?: boolean;
}
function foo(args: iFooArgs): string {
if(args.bypass){
return 'bypass';
}
if(args.snapshot){
return 'snapshot';
}
return args.text;
}
console.debug(foo({text: 'test'}));
console.debug(foo({text: 'bypass?', bypass: true}));
console.debug(foo({text: 'snapshot?', snapshot: true}));
UPDATE: This can be solved cleanly using intersection types:
class MyClass {
constructor() {
this.foo = function () {
return "this is a"
};
this.foo.bar = function() {
return "this is b"
};
}
public foo: Function & { bar?: () => string };
}
const obj = new MyClass();
console.log(obj.foo());
console.log(obj.foo.bar());
Test it in the Typescript Playgroung
This is possible because by using the & sign in the type definition of the class method we are fussing together both a Function type and a simple type of an Object that has 1 property bar that is another function itself. However this property must be declared as optional in the second type, that is because Typescript will complain that in the first assignment inside the constructor of the class it doesn't satisfy this extra type. But in reality the next expression in the constructor completes it by assigning the bar property of foo.
--- OLD answer follows, don't use this it doesn't do exactly what the OP wanted
Instead of a method, define a getter accessor method for class member 'foo' that returns an object with a function 'bar'
class MyClass {
get foo(): {bar: (text: string) => string} {
// run any code here that needs to be run before bar()
return {
bar: (text: string) => {
return text + ' FOO!';
}
}
}
}
const obj = new MyClass();
console.log(obj.foo.bar('thing'));
class A{
constructor(name){
this[name] = name ; //to be private so i need this
new B(this);
}
getName(){
return this[name];
}
}
class B(){
constructor(a){
a.getName()// I wants to be like this
}
}
I just want to call the methods without creating a new instance.
If you want to make private data in a class, either use a WeakMap or a closure. this[name] is not private at all, it's completely visible to anything that has access to the instantiated object.
Another problem is that with your return this[name];, the getName function does not have the name variable in scope.
Also, an object's class methods can't be accessed before the object itself is instantiated.
You might want something like this instead:
const A = (() => {
const internals = new WeakMap();
return class A {
constructor(name) {
internals.set(this, { name });
}
getName() {
return internals.get(this).name;
}
}
})();
class B {
constructor(a) {
console.log(a.getName())
}
}
const a = new A('bob');
const b = new B(a);
How to detect whether EcmaScript class has its own constructor?
See code snippet below.
class Person {
constructor(name, age) { // <- Own constructor
this._name = name;
this._age = age;
}
}
class Manager extends Person {
// This class does not have it's own constructor
}
It seems there is no clear way to do this check.
Thank you all for time and help.
It's not bulletproof, but you could convert the constructor to a string, and see if it contains the word constructor( or something similar
function hasOwnConstructor(instance) {
var str = instance.constructor.toString();
return /class(.*?[^\{])\{([\s\n\r\t]+)?(constructor[\s]+?\()/.test(str);
}
class thing1 {
method() { return this; }
}
class thing2 {
constructor(argument) { return this; }
}
var ins1 = new thing1();
var ins2 = new thing2();
console.log( hasOwnConstructor(ins1) ); // false
console.log( hasOwnConstructor(ins2) ); // true
False positives could still be possible, but the regex makes the check quite strict, so it's not very plausable.
Inspect the constructor function.
class Example {
constructor(prop1, prop2) {
this.prop1 = prop1;
this.prop2 = prop2;
}
}
You can pass parameters to your class via the constructor function that establishes properties which can be globally referenced when creating a new instance.
This line would pass int 1 and "test" as values to prop1 and prop2:
const exampleVar = new Example(1, "test");
and might render something like:
<Example prop1=1 prop2="test" />
Hope this helps. If it has a constructor function, there is a constructor, if it does not have the constructor function, then there is no constructor.
Lets say I have a class defined as follows:
class MyClass {
constructor(a, b) {
this._a = a;
this._b = b;
}
get a() {
return this._a;
}
set a(val) {
this._a = val;
}
add() {
return this._a + this._b;
}
}
I want to be able to access and manipulate the getter and setter functions directly at runtime in order to wrap them in additional debugging code. With the 'add' function, I can do this:
let oldAdd = MyClass.prototype.add;
MyClass.prototype.add = function() {
console.log('add called!');
let result = oldAdd.call(this);
console.log('add result: ' + result);
return result;
}
However, I cannot find a way to modify the getter and setter functions in a similar way.
I have tried
let propDef = Reflect.getOwnPropertyDescriptor(MyClass.prototype, 'a');
propDef.get = function() {
// ...
}
But this change does not actually get applied.
Any ideas?
I am also interested to know if it's possible to access and modify the constructor function in the same way.
Yes, you can do that by reconfiguring the properties. Here's an example (see comments):
class MyClass {
constructor(a, b) {
this._a = a;
this._b = b;
}
get a() {
return this._a;
}
set a(val) {
this._a = val;
}
add() {
return this._a + this._b;
}
}
// Use it before redefining
const instance = new MyClass(1, 2);
console.log(instance.a); // 1
instance.a = 2;
console.log(instance.a); // 2
// Redefine the property
const desc = Reflect.getOwnPropertyDescriptor(MyClass.prototype, "a");
const {get: originalGet, set: originalSet} = desc;
desc.get = function() {
const value = originalGet.call(this);
console.log("Debugging 'get' here, a's value is " + value);
return value;
};
desc.set = function(newValue) {
console.log("Debugging 'set' here, a's new value is " + newValue);
originalSet.call(this, newValue);
};
Object.defineProperty(MyClass.prototype, "a", desc);
// Use it after redefining
console.log(instance.a); // 2, after seeing console statement
instance.a = 3; // Triggers another console statement
console.log(instance.a); // 3 (w/ console statement)
The reason what you did didn't work was partially that you never set the new descriptor on the object. The descriptor you get back doesn't provide direct access to the definition within the object, it's just a new object created by getOwnPropertyDescriptor. To make changes effective, you need to set the new descriptor.
You asked below about doing the same for MyClass itself. As you pointed out, in addition to replacing the function, we need to be sure that its properties show up on the replacement for it.
A simple, easy way to do that is to make the new function inherit from the old:
const originalConstructor = MyClass;
MyClass = class MyClass extends originalConstructor {
constructor(...args) {
return new originalConstructor(...args);
}
};
It's a little-known fact that when B extends A, there are two things that happen:
B.prototype's prototype is set to A.prototype (this is commonly known), and
B's prototype is set to A (less well-known)
So any statics on A are available on B through inheritance.
So:
class Base {
feature() {}
static staticFeature() {}
}
class MyClass extends Base {
subFeature() {}
static staticSubFeature() {}
}
const originalConstructor = MyClass;
MyClass = class MyClass extends originalConstructor {
constructor(...args) {
return new originalConstructor(...args);
}
};
console.log(typeof MyClass.staticFeature);
console.log(typeof MyClass.staticSubFeature);
const instance = new MyClass();
console.log(typeof instance.feature);
console.log(typeof instance.subFeature);
Or if you want a really high-fidelity copy, you'd derive from MyClass's prototype and then copy over any MyClass own properties; but it's more complicated:
const originalConstructor = MyClass;
MyClass = class MyClass extends Object.getPrototypeOf(originalConstructor) {
constructor(...args) {
return new originalConstructor(...args);
}
};
Object.getOwnPropertyNames(originalConstructor)
.concat(Object.getOwnPropertySymbols(originalConstructor))
.forEach(name => {
MyClass[name] = originalConstructor[name];
});
Example:
class Base {
feature() {}
static staticFeature() {}
}
class MyClass extends Base {
subFeature() {}
static staticSubFeature() {}
}
const originalConstructor = MyClass;
MyClass = class MyClass extends Object.getPrototypeOf(originalConstructor) {
constructor(...args) {
return new originalConstructor(...args);
}
};
Object.getOwnPropertyNames(originalConstructor)
.concat(Object.getOwnPropertySymbols(originalConstructor))
.forEach(name => {
MyClass[name] = originalConstructor[name];
});
console.log(typeof MyClass.staticFeature);
console.log(typeof MyClass.staticSubFeature);
const instance = new MyClass();
console.log(typeof instance.feature);
console.log(typeof instance.subFeature);
Let's say I have:
class Foo {}
class Bar extends Foo {}
var clazz = Bar;
I figured out that to get Bar there's clazz.prototype.constructor.
How can I find out what is the parent class of Bar?
As commented on the answer by #MattiasBuelens, it should be: obj.constructor and not obj.prototype.constructor as obj.prototype is null (the prototype property exists on the class Bar but not the instances).
As for getting the constructor of Foo, well this is an ugly hack:
let FooCtor = Object.getPrototypeOf(Object.getPrototypeOf(obj)).constructor;
var foo = new FooCtor();
Edit
If you want to do the same thing but with the Bar class instead of instance of it, then:
let FooCtor = Object.getPrototypeOf(Bar.prototype).constructor;
var foo = new FooCtor();
TypeScript 1.8 uses this to extend a class (reduced here for readability):
var __extends = function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = (__.prototype = b.prototype, new __());
};
var TestPlanetModel = (function (_super) {
__extends(TestPlanetModel, _super);
function TestPlanetModel() {
_super.apply(this, arguments);
}
return TestPlanetModel;
}(FrameModel));
Which uses local Function to instantiate the prototype, and that hides the relation between the two classes in that closure.
Thanks to Nitzan for the trick, I only needed to check the class, not the object, so I instantiate it to get to the prototype:
var clazz = TestPlanetModel;
var parent = Object.getPrototypeOf(Object.getPrototypeOf(new clazz())).constructor;
alert(parent === FrameModel);
I didn't figure out how to do it without instantiating.
I recently released an enhanced version of the TypeScript compiler that lets you know all the reflection metadata of classes and interfaces, both at coding time and runtime. The following code suits your needs:
class MySuper {
id: number;
constructor(n: number) {
console.log("MySuper instantiated with param: " + n);
this.id = n;
}
}
class MySub extends MySuper {
name: string;
}
let sub: Class = MySub.getClass();
if(sub.extends) {
let superCtor = sub.extends.getConstructor<MySuper>(); //type param is optional, you can get "any" by default.
//let's instantiate it!!!
let superObj = new superCtor(3);
console.log("superObj.id = " + superObj.id);
}
and this is the output:
$ node main.js
MySuper instantiated with param: 3
superObj.id = 3
You can find my project here.
You can use "Object.getPrototypeOf (AnyClass)"
to return the super-class of AnyClass:
class Foo {}
class Bar extends Foo {};
let SuperClassOfBar = Object.getPrototypeOf (Bar);
if (SuperClassOfBar === Foo)
{ console.log
(`Superclass of Bar is Foo`); // yes it is
}