Search code examples
typescript3.0

Typescript, How to avoid code duplication in constructor?


Consider this class that is used as a data model in a Model-View-Controller scenario (I'm using TypeScript 3.5):

export class ViewSource {
    private viewName : string;
    private viewStruct : IViewStruct;
    private rows : any[];
    private rowIndex : number|null;

    constructor(viewName : string) {
        // Same as this.setViewName(viewName);
        this.viewName = viewName;
        this.viewStruct = api.meta.get_view_struct(viewName);
        if (!this.viewStruct) {
            throw new Error("Clould not load structure for view, name=" + (viewName));
        }
        this.rows = [];        
        this.rowIndex = null;
    }

    public setViewName = (viewName: string) => {
        this.viewName = viewName;
        this.viewStruct = api.meta.get_view_struct(viewName);
        if (!this.viewStruct) {
            throw new Error("Clould not load structure for view, name=" + (viewName));
        }
        this.rows = [];        
        this.rowIndex = null;
    }

    public getViewStruct = ():IViewStruct => { return this.viewStruct; }

    public getCellValue = (rowIndex: number, columnName: string) : any => {
        const row = this.rows[rowIndex] as any;
        return row[columnName];
    }

}

This is not a complete class, I only included a few methods to demonstrate the problem. ViewSource is a mutable object. It can be referenced from multiple parts of the application. (Please note that being a mutable object is a fact. This question is not about choosing a different data model that uses immutable objects.)

Whenever I want to change the state of a ViewSource object, I call its setViewName method. It does work, but it is also very clumsy. Every line of code in the constructor is repeated in the setViewName method.

Of course, it is not possible to use this constructor:

constructor(viewName : string) {
    this.setViewName(viewName);
}

because that results in TS2564 error:

Property 'viewStruct' has no initializer and is not definitely assigned in the constructor.ts(2564)

I do not want to ignore TS2564 errors in general. But I also do not want to repeat all attribute initializations. I have some other classes with even more properties (>10), and the corresponding code duplication looks ugly, and it is error prone. (I might forget that some things have to bee modified in two methods...)

So how can I avoid duplicating many lines of code?


Solution

  • I think the best method to avoid code duplication in this case would be to create a function that contains the initialization code, but instead of setting the value, it retunrs the value that need to be set.
    Something like the following:

    export class ViewSource {
        private viewName : string;
        private viewStruct : IViewStruct;
        private rows : any[];
        private rowIndex : number|null;
    
        constructor(viewName : string) {
            const {newViewName, newViewStruct, newRows, newRowIndex} = this.getNewValues(viewName);
            this.viewName = newViewName;
            this.newViewStruct = newViewStruct;
            // Rest of initialization goes here
        }
    
        public setViewName = (viewName: string) => {
            const {newViewName, newViewStruct, newRows, newRowIndex} = this.getNewValues(viewName);
            // Rest of initialization goes here
        }
    
        privat getNewValues = (viewName) => {
            const newViewName = viewName;
            const newViewStruct = api.meta.get_view_struct(viewName);
            if (!newViewStruct) {
                throw new Error("Clould not load structure for view, name=" + (viewName));
            }
            const newRows = [];        
            const newRowIndex = null;
            return {newViewName, newViewStruct, newRows, newRowIndex};
        }
    
    }
    

    This way the only thing you duplicate is setting the values, not calculating them, and if the values calculations will get more complicated you can simply expand the returned value.