[FIXED] TypeScript – Copy-Constructor (Multiple constructor implementations are not allowed)

Issue

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);
  }
}

Solution

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.

Answered By – Top-Master

Answer Checked By – Terry (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published