import type
export type
원래의 import, export 문법과 동일하나 뒤에 type만 덧붙여주면 된다.
import type { SomeThing } from "./some-module.js";
export type { SomeThing };
🚨 import type으로 받아온 class는 확장이 불가능하다.
// 클래스가 선언된 파일
export class sampleTypes {
first!: string;
second!: number;
third!: boolean;
}
export class sampleTypes2 {
first!: string;
second!: number;
third!: boolean;
}
// 클래스가 import 되어 확장된 파일
import { sampleTypes } from "types/new";
import type { sampleTypes2 } from "types/new";
class newTypes extends sampleTypes {
hi!: string;
hello!: number;
}
class newTypes2 extends sampleTypes2 {
hi!: string;
hello!: number;
}
// 에러 발생!
// 'sampleTypes2'은(는) 'import type'을 사용하여 가져왔으므로 값으로 사용할 수 없습니다.
🚨 기본 import와 default import는 한 번에 할 수 없다!
export class sampleTypes {
first!: string;
second!: number;
third!: boolean;
}
export default class sampleTypes2 {
first!: string;
second!: number;
third!: boolean;
}
// 에러 발생!
// 형식 전용 가져오기는 기본 가져오기 또는 명명된 바인딩을 지정할 수 있지만,
// 둘 다 지정할 수는 없습니다.
import type sampleTypes2, { sampleTypes } from "types/new";
// TS 코드
import sampleTypes from 'types/new'
let newType: sameTypes = // ~
// 변환 후 JS 코드
// type import 부분은 다른 부수효과를 발생 시키지 않을 것으로 예상되어 삭제됨
let newType = // ~
TS 코드가 컴파일 될 때, 인터페이스와 타입은 사라져서 방출된 결과물에 존재하지 않는다.
만약 import가 이를 위해서만 이루어졌다고 파악될 경우 JS로 변환된 파일에는 해당 import 선언문도 들어가지 않는다.
// 타입이 선언된 파일
export type sampleTypes = {
first: string;
second: number;
third: boolean;
};
export const showTypes = (config: sampleTypes) => {
console.log(config);
};
// 타입을 호출하는 파일
import { sampleTypes, showTypes } from "types/new";
transpileModule
API, Babel을 통해 프로젝트의 모듈을 하나씩 컴파일하려 할 때 생기는 문제가 있다.// import 된 후 export 되는 경우 이것은 단순 타입이 맞을까? 아니면 값으로 취급되어야 할까?
import { reExportedType } from "./foo.js";
export { reExportedType };
정답은 이 파일에 국한된 경우 알 수 있는 방법이 없다는 것이다.
TS 컴파일러는 실시간으로 타입 정보를 생략하려 하지만 re-export 된 타입이 해당 파일에서만 사용된다면 이것을 인지하지 못해 문제가 발생한다.
이러한 이유로 TS의 transpileModule
API, Babel은 reExportedType
이 단순히 타입으로만 사용된 경우에도 제대로 동작하지 못하게 된다.
이는 타입 체크 과정의 안정성을 저해하고, 실제 빌드 이후 결과물의 용량을 불필요하게 늘릴 수 있다.
// 오직 타입만 선언하였으므로 import elision에 의해 삭제된다.
import { SomeTypeFoo, SomeOtherTypeBar } from "./module-with-side-effects";
// 따라서 이 선언문이 반드시 함께 사용되어야 한다.
import "./module-with-side-effects";
두 번째 선언문이 존재하지 않을 경우,
webpack은 변환된 JS코드를 실행할 때에 존재하지 않는 것에 대한 import의 결과로 에러를 발생시킨다.
만약 부수효과를 포함하고 있는 모듈에서 타입을 import 했다면 이 import 구절은 삭제되어 부수효과도 실행되지 않는 문제가 생긴다.
import 선언문이 명시적으로 type-only로 지정되어 있지 않을 경우 방출 시 컴파일러가 import 선언 삭제를 멈추도록 하였다.
이를 통해 부수 효과를 위해 imports를 보존하고 싶었던 사용자들이 필요한 import 선언문만 보존할 수 있었다.
또한 단일 파일 단위로 코드를 변환할 때에 re-export가 타입 정보만을 내보내는 경우 해당 구문을 삭제하도록 식별자를 명시할 수 있는 기능을 제공하였다.
export { T } from './mod'
를 사용하여 타입 T를 re-export 할 수 있었다.하지만 --isolatedModules
플래그의 도입으로 이 방식은 사용되지 않기 시작했다.
isolatedModules
isolatedModules
플래그를 도입하면 타입의 re-exporting 자체를 에러로 처리하므로 단일 파일 변환 시 모듈 간 의존성을 엄격하게 제한할 수 있게 되었다.단순히
isolatedModules
만 사용할 경우의 문제점
isolatedModules
만을 사용하면서 re-exporting을 하려면 아래와 같은 방식을 사용해야 했다.import { sampleType } from "./a";
export type sampleType = sampleType;
이 방식을 통해 모듈에서 타입 정보만을 가져오므로 불필요한 값이 메모리에 로딩되는 문제를 해결하였다.
import type
을 사용할 경우 이러한 import는 항상 컴파일 과정에서 완전히 제거되어 런타임에는 남아있지 않게 된다.
export type
또한 타입의 맥락에서만 사용되는 export 구문이므로, 변환 과정에서 삭제된다.
명시적으로 타입만을 가져온다고 보장되므로 JS 변환 시 삭제된다.
Babel 등의 도구가 isolatedModules
옵션을 통해 코드를 추정하기 더 용이해진다.
import type
을 통해 명시적으로 작성할 수 있으므로 작동하는 플래그이다.remove
preserve
error