I have react component and I need make instance of class like observable.
#observer
class A extend React.Component {
// make observable
instance = new B();
render (){
<div>
<div onClick={() => this.instance.setName('Joe')}/>
// should update
<div>{this.instance.name}</div>
</div>
}
}
Class that we inject:
class B {
#observabe
_name: string
#action
setName(name: string) {
this._name = name;
}
}
Okay, I just forgot to bind context. You can use either arrow function or #action.bind.
Related
I am using typescript and i am writing a custom decorator for one of my angular class. I want to access the base class method in the child class decorator. Or access base class methods using the child class prototype. Is there any way to do this? Problem explained in detail below.
Scenario
I have a base class which is like
export class Base {
public init() {
console.log('My base class function');
}
}
And i have a derived class which extends this base class
export class Child extends Base {
}
What i am trying to do
I am trying to write a decorator for the derived class something like
#TestDecorator(['init'])
export class Child extends Base {
}
which will call the init method from the base class.
What is the issue
To do the above scenario, i have written code something like below
export function Tool<T extends Base>(methods: any[]) {
return function (target: Function) {
methods.forEach((item) => {
if (item === 'init') {
target.super.init() // Stuck here
}
})
}
}
I am not understanding how to make the following line work
target.super.init() // Stuck here
Please help me with the solution. I am stuck. Thanks
I believe you are looking for something like this:
export function Tool<T extends Base>(methods: any[]) {
return function (target: Function) {
return class extends target {
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
To expand on Paulpro's answer, since the decorator function is returning a substitute for the constructor of the class that it is decorating, it must maintain the original prototype.
In the following example, there is an error due to the missing init() method in TestDecorator<Base>.
Typescript Playground Demo
class Base {
public init() {
console.log('My base class function');
}
}
function TestDecorator<T extends Base>(methods: any[]) {
return function (target: any) {
return class extends target {
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
#TestDecorator(['init']) // Error: Property 'init' is missing in type 'TestDecorator<Base>.(Anonymous class)' but required in type 'Child'.
class Child extends Base {
}
let c = new Child();
Corrected Decorator
function TestDecorator<T extends Base>(methods: any[]) {
return function (target: any) {
return class extends target {
init() {} // Define init()
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
Class Decorators
If the class decorator returns a value, it will replace the class declaration with the provided constructor function.
NOTE: Should you choose to return a new constructor function, you must take care to maintain the original prototype. The logic that applies decorators at runtime will not do this for you.
The problem is pretty simple I think.
I have my custom decorator which need some object to proper running.
This object is some service which I have provided for my component. So that I want to pass this service into the decorator.
This is some example of code:
#Component({...})
export class TestComponent {
constructor(private service: TestService){}
#MyDecorator(service)
run(){
...
}
}
Obviously, this approach is bad because the service isn't accessible at the moment when is passed in MyDecorator.
What should I do?
I know I'm a little bit late, but i solved the problem by wrapping it in a function.
// OBJECT THAT WE WILL PASS
class SomeKindOfObject {}
// DECORATOR
function Decorator(type: ()=>object){
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(target, propertyKey, descriptor);
};
}
// CLASS WRAPPED WITH FUNCTION
function component<T extends object>(classRef: T){
class Component{
#Decorator( () => classRef )
find() {
console.log(classRef);
}
}
return Component;
}
// CLASS USAGE
const comp = new (component(SomeKindOfObject));
comp.find();
// INHERITANCE USAGE
class Component2 extends component(SomeKindOfObject){}
Note - i didn't make this example specifically for angular, but i was having a similar problem in nestjs, hope this helps someone.
This is my main class (it uses a subclass:)
import SubClass from './SubClass'
class MainClass extends classes(SubClass) {
constructor () {
// some code
}
}
window.MainClass = new MainClass()
export default MainClass
This is the subclass:
class SubClass {
constructor () {
this.someMethod = function () {
// some code
}
}
}
export default SubClass
If I want to use a method from the SubClass I can write: MainClass.someMethod.
How to modify this code so I write: MainClass.SubClass.someMethod instead?
So I can write:
MainClass.SubClass.someMethod
MainClass.SubClass2.someMethod
In case I need another SubClass?
I think you need to call super(). And classes() seem doen't need to be added.
When used in a constructor, the super keyword appears alone and must be used before the this keyword is used.
See document
import SubClass from './SubClass'
class MainClass extends SubClass {
constructor () {
super();
console.log(this.someMethod)
}
}
Hope this help
I need to extend the two classes from the same namespace.
for ex:
declare namespace myNameSpace{
class class1{
///some methods will be here
}
class class3 extends class1{
//some method wil be here
}
class class2 extends myNameSpace. class3 {
//some methods will be here
}
export namespace class2 {
//declaration will be here
}
}
i need to extend the 'myNameSpace.class1' class as well as 'class2' namespace.
class newClass extends myNameSpace.class1, myNameSpace.class2 {
constructor() {
super();
}
}
If i call the both the classes, i got an error message
classes can only extend a single class
Is there any other way to fix this issue in typescript.
Is there any other way to fix this issue in typescript.
TypeScript is single inheritance by design.
You can use mixins but you can't override methods (unless you write a custom applyMixins methods)
Using the method:
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
You have to implement (on empty way)
class NewClass implements myNameSpace.class1, myNameSpace.class2 {
// empty implementation
public methodFrom1 : ()=>void;
public methodFrom2 : ()=>number;
constructor() {
// no super()
}
}
now use mixing to actually make it multi extend classes:
applyMixins(NewClass, [myNameSpace.class1, myNameSpace.class2]);
and now you can create the class
const foo = new NewClass()
foo.methodFrom1() // actually calls nameSpace.class1.prototype.methodFrom1
I have TypeScript (JavaScript) class like this:
import * as React from 'react'
export default
class StyleableComponent<PROPS, STATE> extends React.Component<PROPS, STATE> {
protected classes: any
static style: any
someMethod() {
const ctor = this.constructor
console.log(this.constructor.style)
}
}
and TypeScript throws this error:
ERROR in ./src/StyleableComponent.ts
(11,38): error TS2339: Property 'style' does not exist on type 'Function'.
But, obviously, you can see that static style: any is declared in the class definition.
So, how do we work with this.constructor properly? Note, this.constructor can be the constructor of a class that extends StyleableComponent, so this.constructor may not be === StyleableComponent, but it should have the style property because it extends from StyleableComponent.
For example,
interface P {...}
interface S {...}
class Foo extends StyleableComponent<P,S> {...}
console.log(new Foo)
^ this.constructor will be Foo, and the StyleableComponent class needs to look at Foo.style.
So how do I do this? Do I need to use a extra template type parameter somehow?
Static Property V.S Instance Property Inheritance
If you want read static property from subclass,you can do it with 3 ways.for more details you can see Test section.
because Object.getPrototypeOf(..)'s return type is any, so you can access style directly,for example:
someMethod() {
let style = Object.getPrototypeOf(this).constructor.style;
}
because this.constructor's return type is a Function, so you must assign it to a any variable at first,for example:
someMethod() {
let cotr:any=this.constructor;
let style = cotr.style;
}
because Function is an interface you can expand it in typescript,for example:
declare global{
interface Function{
style:any;
}
}
someMethod() {
return this.constructor.style;
}
and you can also do it with replace static property with instance property instead.if you want read subclass style property you must define the property on constructor,then the subclass can choose define its style by pass the style to the superclass or not at all.for example:
constructor(protected style:any="default"){
}
the interesting is that the subclass behavior are all the same except the style property.In design view, if you use the static style properties you must define another subclass to achieve it,this will tends to many subclass with diff styles.but when use instance property style,you can do it by pass the style with optional for different style only.for example:
let bar=new Bar();//use parent's style
let baz=new Bar(null,null,"baz");//use it owned style
and you can also reject others to pass their style by pass the style in constructor of the subclass.for example:
constructor(){
super("style");
}
Tests
import * as React from 'react';
declare global {
interface Function {
style: any
}
}
describe('static inheritance', () => {
class StyleableComponent<P, S> extends React.Component<P, S> {
protected classes: any;
static style: any;
constructor(props?: P, context?: any, public style: any = "default") {
super(props, context);
}
someMethod() {
//dangerous if subclass not define static style property
//todo:the 1st approach
// let style = Object.getPrototypeOf(this).constructor.style;
// return style;
//todo:the 2nd approach
// let cotr: any = this.constructor;
// return cotr.style;
//todo:the 3nd approach,you must declare style in Function interface
return this.constructor.style;
}
}
class Foo extends StyleableComponent<any, any> {
static style = "foo";
constructor(props?: any, context?: any) {
super(props, context, Foo.style);
}
}
class Bar extends StyleableComponent<any, any> {
}
test('access style from subclass', function () {
let foo = new Foo();
expect(foo.someMethod()).toBe(Foo.style);
});
test('return undefined if subclass not define style', function () {
let bar = new Bar();
expect(bar.someMethod()).toBeUndefined();
});
test('replace static style with instance property', function () {
let foo = new Foo();
let bar = new Bar();
let baz = new Bar(null, null, "baz");
expect(foo.style).toBe("foo");
expect(bar.style).toBe("default");
expect(baz.style).toBe("baz");
});
});