개요

자식 클래스의 생성자 함수에서 호출하는 super()는 부모 클래스의 생성자에서 실행되지 않습니다.

class MotherClass {
  public constructor() {
    //여러가지 초기화 과정 
		this.init();
	}

  //이 함수는 외부에서도 호출됩니다.
	public init(): void {
        console.log("init called in mother class");
	}
}

class ChildClass extends MotherClass {
		private readonly field1;

    public constructor(field1: string) {
        super();
        //여러가지 초기화 과정   
			  this.field1 = field1;
        this.init();
    }

    public init(): void {
        console.log("field is " + this.field1);
    }
}

new ChildClass("'hello world!'");
//field is undefined
//field is 'hello world!'

자식 클래스를 생성했을 때,

생성자 함수는super()를 호출하고 부모 클래스의 생성자 함수에서 여러 초기화 과정을 거친 후 부모의init()를 호출하고 부모 클래스의 생성자 함수를 마친 후 여러가지 초기화 과정을 거친 후에 자신의init()를 호출해서 field1를 사용하는걸 기대했습니다.

하지만 실제로는 자식의 init()가 두 번 호출되었습니다. 심지어 첫번째는 field1가 undefined인 상태였습니다. 이것은 super()를 타고 부모 생성자 함수에 올라가서 초기화를 거치고 init()를 호출하는 것이 실상은

//in ChildClass#constructor

{
  //부모의 여러가지 초기화 과정
  this.init();
  //자식의 여러가지 초기화 과정
	this.field1 = field1;
  this.init();
}

즉, super() 의 실행 위치가 MotherClass가 아닌 ChildClass였습니다. field1가 초기화되기 전에 this.init() 를 호출하여 field1를 참조하거나 사용했으니 예상치 못한 결과가 생길 것입니다.

타입스크립트는 이것을 예측할 수 없었습니다.

해결책 후보

생각할 수 있는 해결책들

1. 파라미터 프로퍼티 사용하기

Documentation - Classes

타입스크립트에선 dart의 this 매개변수와 비슷하게 생성자 파라미터에 직접 필드를 접어넣을 수 있습니다. 생성자는 별도의 초기화 과정(this.field = field)을 작성할 필요 없이 자동으로 초기화해줍니다. 이는 초기화할 필드 수가 많을수록 유용합니다. 다만 optional bag와 같이 named paramter의 대체용으로 쓰던 객체 매개변수와 상충되는 문법이라 다시 순서에 의존하게 됩니다.

파라미터 프로퍼티는 위치상 생성자 함수의 가장 윗부분에 있기에 가장 먼저 초기화되어 super() 안에서도 초기화된 field1를 사용할 것 같지만, 실제로 테스트해보면 그렇지 않음을 알 수 있습니다.

https://www.typescriptlang.org/ko/play?#code/MYGwhgzhAECyD2AXAFgUwE4GFxWgbwChpoAHAVwCMQBLYaYeAOwkXTOEXnQAoBKfIsSHEA9CMABvYAquwBrj0QDejgG-bAADWAByeiAIWcAMdYBU16IB+awKgTxAgEgTKahAB01RtUR8A3KYC+BQaJGAXceiALVcAYQ4ApTdCAHmPigAujgDiDgCPN0IAca4A5s4CULYATTYAnTVam5FS00Lb2fABc0ABu8NQAJgLCwgzM8CCoViDwAObcAET5iPRgII1VttAAtkho6PQ4EB28ziZubgSgkDCYyNQgFdgr0KgAHoiojBUwCCgY27iEwtk0dHUsbBxc3CTo1CVgh9DoqGAVTBAAE9oAAzaioTYARmKj1srX41xqxAgZBIGCcHhqYkUqg0On0RmMyOIFms3UxwkWN0odzydgcvGKZUq1RJDwaTRa7Q64MhgxgHWgAGpoGSrHzobMsQt3IxUAB3aBrDZbKadADkaH68GgCq4mwAhBqZo4gA

https://www.typescriptlang.org/ko/play?#code/MYGwhgzhAECyD2AXAFgUwE4GFxWgbwChpoAHAVwCMQBLYaYeAOwkXTOEXnQAoBKfIsSHEA9CMABvYAquwBrj0QDejgG-bAADWAByeiAIWcAMdYBU16IB+awKgTxAgEgTKahAB01RtUR8A3KYC+BQaJGAXceiALVcAYQ4ApTdCAHmPigAujgDiDgCPN0IAca4A5s4CULYATTYAnTVam5FS00Lb2fABc0ABu8NQAJgLCwgzM8CCoViDwAObcAET5iPRgII1VttAAtkho6PQ4EB28ziZubgSgkDCYyNQgFdgr0KgAHoiojBUwCCgY27iEwtk0dHUsbBxc3CTo1CVgh9DoqGAVTBAAE9oAAzaioTYARmKj1srX41xqxAgZBIGCcHhqYkUqg0On0RmMyOIFms3UxwkWN0odzydgcvGKZUq1RJDwaTRa7Q64MhgxgHWgAGpoGSrHzobMsQt3IxUAB3aBrDZbKadADkaH68GgCq4mwAhBqZo4gA