Search code examples
typescriptcopy-constructor

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


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.