오진수 블로그
카테고리
  1. 전체 (13)
  2. 포트폴리오 (3)
  3. 프로그래밍 (10)
profile image오진수 · 프로그래밍 · 

[TS] Cannot access '_' before initialization 오류 import type으로 해결하기


ㅇㅇ.png


"Cannot access '_' before initialization" 오류는 말 그대로 무언가를 초기화하기 전에 사용하는 경우 발생합니다.


순환참조 문제

대표적으로는 클래스 간 의존성을 잘못 설계한 경우입니다. 다음과 같은 예를 생각해 볼 수 있겠습니다.


circular_dependency.drawio.png


Hyundai 클래스가 Car를 상속하고, Car 클래스가 다시 Vehicle 클래스를 상속하고, Vehicle 클래스가 Hyundai 클래스를 참조하는 경우입니다. 순환참조(circular reference) 문제라고도 합니다.


이때 만약 Hyundai 객체를 생성하려고 한다면(new Hyundai()) "Cannot access 'something' before initialization" 오류가 발생할 겁니다.


런타임에 Hyundai 객체를 생성하기 위해서는 먼저 생성할 Hyundai 클래스를 올바르게 정의해야 합니다. 그런데 Hyundai 클래스를 정의하기 위해서는 먼저 Car를 Import해야 하죠. Car도 마찬가지입니다. Car 클래스를 정의하기 위해서는 Vehicle 클래스를 Import해야 합니다. 그리고 여기서는 다시 Vehicle 클래스가 Hyundai 클래스를 import하고 있습니다.


예제가 단순하기 때문에 얼핏 그다지 벌어지지 않을 실수 같아 보입니다. 하지만 분명 클래스 사이 관계가 복잡한 애플리케이션을 구현하다 보면 종종 생기는 일입니다.


import type 구문 활용법

물론 클래스 사이 관계를 다시 설계할 수도 있지만 그게 어려울 때도 있습니다. 이러한 경우 힌트는 런타임에 타입 정보를 제거하는 타입스크립트 특성을 활용하는 방법입니다.


만약 Vehicle 클래스가 Hyundai 클래스를 다음과 같이 타입 선언 용도로 사용하고 있다고 가정해 봅시다.


import { Hyundai } from "./hyundai";

export class Vehicle {
  hyundai?: Hyundai;
}


이러한 경우 import type 구문을 활용해서 런타임에 Hyundai에 대한 의존성을 없앨 수 있습니다. 그러면 오류도 사라지게 됩니다. 더 이상 런타임에 Vehicle을 정의하기 위해 Hyundai를 불러올 필요가 없어지기 때문입니다.


import type { Hyundai } from "./hyundai";

export class Vehicle {
  hyundai?: Hyundai; // 런타임에 Hyundai 타입이 없어집니다. 
}


거듭 말하지만 여기서 import type 구문을 활용할 수 있는 이유는 Hyundai 클래스를 타입 선언 용도로 사용하고 있기 때문입니다. 만약 다음과 같이 상속하하는 클래스를 import type 구문으로 불러온다면 어떻게 될까요?


import type { Vehicle } from "./vehicle";

export class Car extends Vehicle {}


안타깝게도 이런 식으로는 순환참조 문제를 해결할 수 없습니다. 아예 컴파일되지 않는 코드입니다. 다음과 같은 컴파일 오류가 뜰 겁니다.


'Vehicle' cannot be used as a value because it was imported using 'import type'.ts(1361)


import type 구문을 이용해 불러왔기 때문에 타입으로만 사용할 수 있지 값으로는 사용할 수 없다는 이야기입니다. extends로 상속하기 위해서는 클래스를 값으로 사용해야 합니다. 상속이란 타입뿐만 아니라 구현까지도 물려받아야 하기 때문입니다.


한발짝 나아가서 이러한 특성을 이해했다면 implements 키워드를 사용하는 경우에는 import type을 이용해 불러온 클래스도 상속이 가능하다는 점도 추론할 수 있습니다. implements로 클래스를 상속하는 경우는 해당 클래스를 인터페이스처럼 사용하기 때문입니다. 그리고 타입스크립트에서 인터페이스는 런타임 때 제거되죠.

댓글 0

프로그래밍 카테고리 다른 글