I tried the following way to create an interface and implement it.
class AInterface {
constructor () {
if (!this.methodA) {
throw new Error('class should implement a methodA() method')
} else if (!this.methodB) {
throw new Error('class should implement a methodB() method')
}
}
}
export default AInterface
implemented that in a class by extending it. (Note that I have used ts-mixer to have multiple inheritance.
import AInterface from './AInterface'
import { Mixin } from 'ts-mixer'
class ClassA extends Mixin(AnotherClass, AInterface) {
constructor () {
super()
}
methodA () {
return 'test'
}
methodB () {
return 'test'
}
}
export default ClassA
This will throw the error class should implement a methodA() method. Which means the check I do in the interface fails if (!this.methodA).
This works fine when I remove the Mixin and extend only the interface. (class ClassA extends AInterface)
Is there a better way to do this or how can I fix this?
Node version - 14
The problem appears to be that the constructor of AInterface does not get the right this. So it does not see the methodA or methodB.
The workaround is to avoid doing that check in the constructor.
import AInterface from "./AInterface.mjs";
import { Mixin, settings } from "ts-mixer";
settings.initFunction = "init";
class ClassA extends Mixin(AnotherClass, AInterface) {}
AInterface.js
class AInterface {
init () {
if (!this.methodA) {
throw new Error('class should implement a methodA() method')
} else if (!this.methodB) {
throw new Error('class should implement a methodB() method')
}
}
}
export default AInterface
Notice the use of init() method above.
Related
In a library that I wish to extend without modifying its code, several classes inherit from the same imported one. That is in this BaseClass I would need to overwrite a specific method.
In the library (written in TypeScript) :
import { BaseClass } from './base_class';
export class ClassA extends BaseClass {}
import { BaseClass } from './base_class';
export class ClassB extends BaseClass {}
…
In the external extension I wish to write :
import { BaseClass } from 'library';
export class ExtendedBaseClass extends BaseClass {
oneMethod() {
const data = BaseClass.prototype.oneMethod.call(this);
// make additional things with data
return data;
}
}
Is there a way for this new ExtendedBaseClass to become the parent of all ClassXs ? At least in a new extended and re-exported version of them without the need to copy their internal code.
Is there a way for this new ExtendedBaseClass to become the parent of all ClassXs?
No.
An alternative might be to replace the one method directly on the base class:
import { BaseClass } from 'library';
const oneMethod = BaseClass.prototype.oneMethod;
Object.defineProperty(BaseClass.prototype, 'oneMethod', {
value() {
const data = oneMethod.call(this);
// make additional things with data
return data;
},
});
There's no way to do exactly what you're asking, but you could achieve the same result by extending each class individually.
ExtendedClassA extends ClassA {
oneMethod() {
// call a shared method if you need to reuse
}
}
// ExtendedClassB, etc
I have declared a class, like:
export default class Utils {
static deepClone(): object {
return {}
}
}
so when I want to use deepClone, I can:
// First One
import Utils from './Utils';
export default class Element {
constructor(){
this.utils = new Utils()
}
create(){
return this.utils.deepClone()
}
}
or:
// Second One
import Utils from './Utils';
export default class Element {
constructor(){
this.utils = Utils
// this is an optional
// in my child class I need't to import Utils
// I can use this.utils.deepClone() directly
}
create(){
return Utils.deepClone()
}
}
I wonder which is a better way to imply Element class
Looking forward to your reply, I can’t thank you enough
The second way is more correct but it has a problem that you should create an instance of Utils class to use each property which isn't a static method.
The output of a class that you didn't create an instance is a function instead of an object of prototypes.
./Utils.js
export default class Utils {
static deepClone(): object {
return {}
}
public deepExtend() {
// deepClone code
}
}
Use in another file:
import Utils from './Utils';
export default class Element {
constructor(){
this.utils = new Utils();
}
create(){
return Utils.deepClone()
}
extend(){
return this.utils.deepExtend()
}
}
I would return export new of the Utils and I will pass it in the constructor of your Element as an Instance, something like:
IUtils = someInterface;
export default class Element {
constructor(utils: IUtils){
this.utils = utils;
}
create(){
return this.utils.deepClone()
}
}
In these way it:
Doesn't create new instances for nothing
Uses other instances that you can pass to your Element class
Is testable
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.
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
So I'm trying to extend a class in node js and the compiler keeps returning the following error:
TypeError: Class extends value #<Object> is not a function or null
I checked that I was exporting the class correctly and I am, any ideas? I'll post my code below:
/handler/venue.js:
var VenueViews = require('../views/venue'); // If I remove this the error will dissapear (as expected)
class Venue {
constructor(data) {
this.setDataHere = data;
}
main () {
var View = new VenueViews(); // This doesn't run
}
}
module.exports = Venue;
/views/venue.js:
var Venue = require('../handlers/venue');
console.log (Venue) // This returns {} ???
class VenueViews extends Venue {
constructor() {
super();
}
}
module.exports = VenueViews;
I know that node supports these es6 features, so I'm unsure why they aren't working?
Edit:
I'm not sure if this is suppose to happen but, when I log my Venue require it returns an empty object {}.
console.log (Venue) // This returns {} ???
So it turns out I had a circular reference in my code, where I was importing the class that was extending, into the class that itself was extending (tongue twister :P).
The obvious fix was to simply remove the extends reference and find another way of doing what I was trying to achieve. In my case it was passing the Venue class properties down into the VenueViews constructor.
E.g var x = VenueViews(this)
In my instance, it was the same issue as #James111 was experiencing (circular import) due to a factory pattern I was trying to set up in Typescript. My fix was to do move the code into files, similar to the following:
// ./src/interface.ts
import { ConcreteClass } from './concrete';
export interface BaseInterface {
someFunction(): any;
}
export class Factory {
static build(): BaseInterface {
return new ConcreteClass();
}
}
// ./src/base.ts
import { BaseInterface } from './interface';
class BaseClass implements BaseInterface {
someFunction(): any {
return true;
}
}
// ./src/concrete.ts
import { BaseClass } from './base';
export class ConcreteClass extends BaseClass {
someFunction(): any {
return false;
}
}
I had faced similar issue, after checking all the workaround finally issue got resolved by deleting the node_modules folder and run npm i.