TypeScript - Copy-Constructor (Multiple constructor implementations are not allowed)
Asked Answered
E

2

-1

Does TypeScript support copy-constructor (like for example C++ does)?

When the answer is no (or not yet), then what is the best practice to initialize our base-class (which we extend), and copy from an existing instance (of the same base-class type).

I tried but got error:

Multiple constructor implementations are not allowed

Current Code:

Currently our code uses the manually declared copy() method of our base-class which does require the base-class to be already initialized,

But our base-class (ShopConfig) has some rather expensive operations in its constructor, which are already done once, and would not be required if there was a copy-constructor concept in TypeScript implemented.

class ShopConfig {
    public apiKey: string;
    public products: any;

    constructor(apiKey: string = 'trial') {
        this.apiKey = apiKey;
        //Fetch list of products from local Data-Base
        this.products = expensiveDataBaseQuery();
    }

    protected copy(other: ShopConfig) {
        for (const field in other) {
            if (other.hasOwnProperty(field)) {
                this[field] = other[field];
            }
        }
    }
}

class ShopManager extends ShopConfig {

  constructor(config: ShopConfig) {
      super();
      super.copy(config);
      console.log('ShopManager configurations:', config);
  }
}
Erotic answered 10/2, 2019 at 5:34 Comment(0)
E
0

No, type-script has no copy-constructor.

But I did workaround it, like:

  • First, modify the constructor argument-types (of class, in OP's case ShopConfig) to allow both constructor and copy-constructor types (using combination of | operators in type).
  • Then, in constructor body/logic check parameter-type(s):
    • For class-types: myParam instanceof ClassName
    • For primitive-types: typeof myParam === 'primitiveName'
  • Finally, based on parameter-type decide if we are constructing or copying, and handle related task (I mean, copy if was used as copy-constructor, you get the idea).

Example

class ShopConfig {
    public apiKey: string;
    public products: any;

    constructor( v: ShopConfig
            | string | String
            | null
            = 'trial'
    ) {
        if ( ! v) {
            // ... Invalid parameters detected ...

            throw new Error('ShopConfig: expected API-Key or existing instance');
        } else if (v instanceof ShopConfig) {
            // ... Copy-constructor detected ...

            // Customize below to match your needs.
            for (const field in v) {
                if (v.hasOwnProperty(field)) {
                    this[field] = v[field];
                }
            }
        } else if (typeof v === 'string' || v instanceof String) {
            // ... Normal-constructor detected ...

            this.apiKey = v.toString();
            // Fetch list of products from local Data-Base
            this.products = expensiveDataBaseQuery();
        }
    }
}

class ShopManager extends ShopConfig {

    constructor(config: ShopConfig) {
        super(config);
        console.log('ShopManager configurations:', config);
    }
}

Note that there is nothing complicated in the above copy-related-logic, which has very few lines, else we would create a _copy(...) method (in base-class, maybe protected instead of private, to be overridable), and call that from same-class' constructor (right after type-check).

Also, maybe ShopConfig should be last in constructor's type-chain, to prioritize normal-constructing over copy-constructing, but we place it first to ensure users notice that copying is possible.

Erotic answered 10/2, 2019 at 7:7 Comment(0)
M
-1

The simple answer would be NO. Typescript has no copy constructor.

For longer answer.

we can imitate like this.

  class A {
       // getter or setter as you want
       public propertyA: string;
       public propertyB: string;
       private copy(object: A) {
           object.propertyA = this.propertyA;
           object.propertyA = this.propertyB;
       }

       public clone() {
          const obj = new A();
          this.copy(obj);
          return obj;
       }
    }

As you mention. there's heavy task in constuctor. I'd prefer to move it to another function maybe called initialize and call it outside of the constructor.

You can use Factory method pattern for mitigate needing call initialize for every creating object. or you can pass parameter to constructor to not call initialize when create the new object inside clone method.

I'd prefer this more than cloning dynamically as the above answer which lead to needless complexity.

but if. you found you need to clone a lot. (Maybe you try to implement Prototype design pattern) I'd suggest to use cloneDeep from lodash.

https://lodash.com/docs/4.17.15#cloneDeep

Monkery answered 15/8, 2022 at 18:8 Comment(9)
OP wants to construct base and/or parent class as copy, but you are just copying an already constructed instance.Erotic
Moving expensive logic to some-other-method then manually calling later means a lot more room for mistakes, so sorry, this does not come anywhere near a copy-constructor's inheritance and human-readability, which is what's asked for (and has already answer for).Erotic
First of all, I'd like to say sorry. I didn't mean to mad you or tell you the answer is bad . needless complexity is one of symton of 7 symtons. every developer (including me) makes it evitable in part of the software. We can reduce it, move to another part but it really hard to get of rid all of them. What I try to say is it's not a bad thing or severe problem. it's usually happened. but sorry if that word makes you feel bad.Monkery
My intention wants to say that there's **no copy constructor ** in typescript as you edited the answer which is correct. I totally agree with it. the second point is copy constructor has two types. implicit and explicit. What I show in the above answer is explicit. You declare it manually what properties you want to copy. Copy constructor is known for the prototype pattern. IMO, after I read it again. seem like you'd like to avoid expensive tasks in constuctor.Monkery
constructor should be simple like assigning initial properties. this is the point of constuctor. if you violate it. the problem will be like in your case. You want an object, but you instantiate it due to an expensive task. the case you want to instantiate, but don't want it to do something else is sound like an object screaming to separate some task to another object. To make it clear, It's like violating SRP (Single responsibility). again this is normal and you're not bad. I also do it and I think other developers do it from time to time.Monkery
You can separate expensive tasks from another object. Maybe it's a singleton and does expensive tasks only once. or you can just separate classes and do the task with the object. but that's suitable for another thread. some-other-method then manually calling it not a bad thing. If you want to make sure users of this class always calls the method. One simple way is to create another function to create the object and call the method. The user tries to create the object using the method instead. BTW. copy constructor, constructor. it should be simple as possible.Monkery
A constructor and/or method allowing multiple types for same parameter is the very basics of type-script.Erotic
And if that's complex or violates some patterns in your opinion, don't use type-script, or at least accept that it's part of the language (not other-developers' mistake).Erotic
Having to call separate-method all over the project (just to construct), is far more complicated and un-maintainable, in my opinion (or maybe most developers' opinion).Erotic

© 2022 - 2024 — McMap. All rights reserved.